Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/symbolic/assumptions.py
4095 views
1
from sage.structure.sage_object import SageObject
2
from sage.rings.all import ZZ, QQ, RR, CC
3
from sage.symbolic.ring import is_SymbolicVariable
4
_assumptions = []
5
6
class GenericDeclaration(SageObject):
7
8
def __init__(self, var, assumption):
9
"""
10
This class represents generic assumptions, such as a variable being
11
an integer or a function being increasing. It passes such
12
information to maxima's declare (wrapped in a context so it is able
13
to forget).
14
15
INPUT:
16
17
18
- ``var`` - the variable about which assumptions are
19
being made
20
21
- ``assumption`` - a Maxima feature, either user
22
defined or in the list given by maxima('features')
23
24
25
EXAMPLES::
26
27
sage: from sage.symbolic.assumptions import GenericDeclaration
28
sage: decl = GenericDeclaration(x, 'integer')
29
sage: decl.assume()
30
sage: sin(x*pi)
31
sin(pi*x)
32
sage: sin(x*pi).simplify()
33
0
34
sage: decl.forget()
35
sage: sin(x*pi)
36
sin(pi*x)
37
38
Here is the list of acceptable features.
39
40
::
41
42
sage: maxima('features')
43
[integer,noninteger,even,odd,rational,irrational,real,imaginary,complex,analytic,increasing,decreasing,oddfun,evenfun,posfun,constant,commutative,lassociative,rassociative,symmetric,antisymmetric,integervalued]
44
"""
45
self._var = var
46
self._assumption = assumption
47
self._context = None
48
49
def __repr__(self):
50
"""
51
EXAMPLES::
52
53
sage: from sage.symbolic.assumptions import GenericDeclaration
54
sage: GenericDeclaration(x, 'foo')
55
x is foo
56
"""
57
return "%s is %s" % (self._var, self._assumption)
58
59
def __cmp__(self, other):
60
"""
61
TESTS::
62
63
sage: from sage.symbolic.assumptions import GenericDeclaration as GDecl
64
sage: var('y')
65
y
66
sage: GDecl(x, 'integer') == GDecl(x, 'integer')
67
True
68
sage: GDecl(x, 'integer') == GDecl(x, 'rational')
69
False
70
sage: GDecl(x, 'integer') == GDecl(y, 'integer')
71
False
72
"""
73
if isinstance(self, GenericDeclaration) and isinstance(other, GenericDeclaration):
74
return cmp( (self._var, self._assumption),
75
(other._var, other._assumption) )
76
else:
77
return cmp(type(self), type(other))
78
79
def assume(self):
80
"""
81
TEST::
82
83
sage: from sage.symbolic.assumptions import GenericDeclaration
84
sage: decl = GenericDeclaration(x, 'even')
85
sage: decl.assume()
86
sage: cos(x*pi).simplify()
87
1
88
sage: decl2 = GenericDeclaration(x, 'odd')
89
sage: decl2.assume()
90
Traceback (most recent call last):
91
...
92
ValueError: Assumption is inconsistent
93
sage: decl.forget()
94
"""
95
from sage.calculus.calculus import maxima
96
if self._context is None:
97
# We get the list here because features may be added with time.
98
valid_features = list(maxima("features"))
99
if self._assumption not in [repr(x).strip() for x in list(valid_features)]:
100
raise ValueError, "%s not a valid assumption, must be one of %s" % (self._assumption, valid_features)
101
cur = maxima.get("context")
102
self._context = maxima.newcontext('context' + maxima._next_var_name())
103
try:
104
maxima.eval("declare(%s, %s)" % (repr(self._var), self._assumption))
105
# except TypeError, mess:
106
# if 'inconsistent' in str(mess): # note Maxima doesn't tell you if declarations are redundant
107
# raise ValueError, "Assumption is inconsistent"
108
except RuntimeError, mess:
109
if 'inconsistent' in str(mess): # note Maxima doesn't tell you if declarations are redundant
110
raise ValueError, "Assumption is inconsistent"
111
else:
112
raise
113
maxima.set("context", cur)
114
115
if not self in _assumptions:
116
maxima.activate(self._context)
117
_assumptions.append(self)
118
119
def forget(self):
120
"""
121
TEST::
122
123
sage: from sage.symbolic.assumptions import GenericDeclaration
124
sage: decl = GenericDeclaration(x, 'odd')
125
sage: decl.assume()
126
sage: cos(pi*x)
127
cos(pi*x)
128
sage: cos(pi*x).simplify()
129
-1
130
sage: decl.forget()
131
sage: cos(x*pi).simplify()
132
cos(pi*x)
133
"""
134
from sage.calculus.calculus import maxima
135
if self._context is not None:
136
try:
137
_assumptions.remove(self)
138
except ValueError:
139
return
140
maxima.deactivate(self._context)
141
else: # trying to forget a declaration explicitly rather than implicitly
142
for x in _assumptions:
143
if repr(self) == repr(x): # so by implication x is also a GenericDeclaration
144
x.forget()
145
break
146
return
147
148
def contradicts(self, soln):
149
"""
150
Returns ``True`` if this assumption is violated by the given
151
variable assignment(s).
152
153
INPUT:
154
155
- ``soln`` - Either a dictionary with variables as keys or a symbolic
156
relation with a variable on the left hand side.
157
158
EXAMPLES::
159
160
sage: from sage.symbolic.assumptions import GenericDeclaration
161
sage: GenericDeclaration(x, 'integer').contradicts(x==4)
162
False
163
sage: GenericDeclaration(x, 'integer').contradicts(x==4.0)
164
False
165
sage: GenericDeclaration(x, 'integer').contradicts(x==4.5)
166
True
167
sage: GenericDeclaration(x, 'integer').contradicts(x==sqrt(17))
168
True
169
sage: GenericDeclaration(x, 'noninteger').contradicts(x==sqrt(17))
170
False
171
sage: GenericDeclaration(x, 'noninteger').contradicts(x==17)
172
True
173
sage: GenericDeclaration(x, 'even').contradicts(x==3)
174
True
175
sage: GenericDeclaration(x, 'complex').contradicts(x==3)
176
False
177
sage: GenericDeclaration(x, 'imaginary').contradicts(x==3)
178
True
179
sage: GenericDeclaration(x, 'imaginary').contradicts(x==I)
180
False
181
182
sage: var('y,z')
183
(y, z)
184
sage: GenericDeclaration(x, 'imaginary').contradicts(x==y+z)
185
False
186
187
sage: GenericDeclaration(x, 'rational').contradicts(y==pi)
188
False
189
sage: GenericDeclaration(x, 'rational').contradicts(x==pi)
190
True
191
sage: GenericDeclaration(x, 'irrational').contradicts(x!=pi)
192
False
193
sage: GenericDeclaration(x, 'rational').contradicts({x: pi, y: pi})
194
True
195
sage: GenericDeclaration(x, 'rational').contradicts({z: pi, y: pi})
196
False
197
"""
198
if isinstance(soln, dict):
199
value = soln.get(self._var)
200
if value is None:
201
return False
202
elif soln.lhs() == self._var:
203
value = soln.rhs()
204
else:
205
return False
206
try:
207
CC(value)
208
except:
209
return False
210
if self._assumption == 'integer':
211
return value not in ZZ
212
elif self._assumption == 'noninteger':
213
return value in ZZ
214
elif self._assumption == 'even':
215
return value not in ZZ or ZZ(value) % 2 != 0
216
elif self._assumption == 'odd':
217
return value not in ZZ or ZZ(value) % 2 != 1
218
elif self._assumption == 'rational':
219
return value not in QQ
220
elif self._assumption == 'irrational':
221
return value in QQ
222
elif self._assumption == 'real':
223
return value not in RR
224
elif self._assumption == 'imaginary':
225
return value not in CC or CC(value).real() != 0
226
elif self._assumption == 'complex':
227
return value not in CC
228
229
def preprocess_assumptions(args):
230
"""
231
Turns a list of the form (var1, var2, ..., 'property') into a
232
sequence of declarations (var1 is property), (var2 is property),
233
...
234
235
EXAMPLES::
236
237
sage: from sage.symbolic.assumptions import preprocess_assumptions
238
sage: preprocess_assumptions([x, 'integer', x > 4])
239
[x is integer, x > 4]
240
sage: var('x,y')
241
(x, y)
242
sage: preprocess_assumptions([x, y, 'integer', x > 4, y, 'even'])
243
[x is integer, y is integer, x > 4, y is even]
244
"""
245
args = list(args)
246
last = None
247
for i, x in reversed(list(enumerate(args))):
248
if isinstance(x, str):
249
del args[i]
250
last = x
251
elif ((not hasattr(x, 'assume') or is_SymbolicVariable(x))
252
and last is not None):
253
args[i] = GenericDeclaration(x, last)
254
else:
255
last = None
256
return args
257
258
def assume(*args):
259
"""
260
Make the given assumptions.
261
262
INPUT:
263
264
- ``*args`` - assumptions
265
266
EXAMPLES:
267
268
Assumptions are typically used to ensure certain relations are
269
evaluated as true that are not true in general.
270
271
Here, we verify that for `x>0`, `\sqrt(x^2)=x`::
272
273
sage: assume(x > 0)
274
sage: bool(sqrt(x^2) == x)
275
True
276
277
This will be assumed in the current Sage session until forgotten::
278
279
sage: forget()
280
sage: bool(sqrt(x^2) == x)
281
False
282
283
284
Another major use case is in taking certain integrals and limits
285
where the answers may depend on some sign condition::
286
287
sage: var('x, n')
288
(x, n)
289
sage: assume(n+1>0)
290
sage: integral(x^n,x)
291
x^(n + 1)/(n + 1)
292
sage: forget()
293
294
::
295
296
sage: var('q, a, k')
297
(q, a, k)
298
sage: assume(q > 1)
299
sage: sum(a*q^k, k, 0, oo)
300
Traceback (most recent call last):
301
...
302
ValueError: Sum is divergent.
303
sage: forget()
304
sage: assume(abs(q) < 1)
305
sage: sum(a*q^k, k, 0, oo)
306
-a/(q - 1)
307
sage: forget()
308
309
An integer constraint::
310
311
sage: var('n, P, r, r2')
312
(n, P, r, r2)
313
sage: assume(n, 'integer')
314
sage: c = P*e^(r*n)
315
sage: d = P*(1+r2)^n
316
sage: solve(c==d,r2)
317
[r2 == e^r - 1]
318
319
Simplifying certain well-known identities works as well::
320
321
sage: sin(n*pi)
322
sin(pi*n)
323
sage: sin(n*pi).simplify()
324
0
325
sage: forget()
326
sage: sin(n*pi).simplify()
327
sin(pi*n)
328
329
If you make inconsistent or meaningless assumptions,
330
Sage will let you know::
331
332
sage: assume(x<0)
333
sage: assume(x>0)
334
Traceback (most recent call last):
335
...
336
ValueError: Assumption is inconsistent
337
sage: assume(x<1)
338
Traceback (most recent call last):
339
...
340
ValueError: Assumption is redundant
341
sage: assumptions()
342
[x < 0]
343
sage: forget()
344
sage: assume(x,'even')
345
sage: assume(x,'odd')
346
Traceback (most recent call last):
347
...
348
ValueError: Assumption is inconsistent
349
sage: forget()
350
351
You can also use assumptions to evaluate simple
352
truth values::
353
354
sage: x, y, z = var('x, y, z')
355
sage: assume(x>=y,y>=z,z>=x)
356
sage: bool(x==z)
357
True
358
sage: bool(z<x)
359
False
360
sage: bool(z>y)
361
False
362
sage: bool(y==z)
363
True
364
sage: forget()
365
sage: assume(x>=1,x<=1)
366
sage: bool(x==1)
367
True
368
sage: bool(x>1)
369
False
370
sage: forget()
371
372
TESTS:
373
374
Test that you can do two non-relational
375
declarations at once (fixing Trac ticket 7084)::
376
377
sage: var('m,n')
378
(m, n)
379
sage: assume(n, 'integer'); assume(m, 'integer')
380
sage: sin(n*pi).simplify()
381
0
382
sage: sin(m*pi).simplify()
383
0
384
sage: forget()
385
sage: sin(n*pi).simplify()
386
sin(pi*n)
387
sage: sin(m*pi).simplify()
388
sin(pi*m)
389
"""
390
for x in preprocess_assumptions(args):
391
if isinstance(x, (tuple, list)):
392
assume(*x)
393
else:
394
try:
395
x.assume()
396
except KeyError:
397
raise TypeError, "assume not defined for objects of type '%s'"%type(x)
398
399
def forget(*args):
400
"""
401
Forget the given assumption, or call with no arguments to forget
402
all assumptions.
403
404
Here an assumption is some sort of symbolic constraint.
405
406
INPUT:
407
408
409
- ``*args`` - assumptions (default: forget all
410
assumptions)
411
412
413
EXAMPLES: We define and forget multiple assumptions::
414
415
sage: var('x,y,z')
416
(x, y, z)
417
sage: assume(x>0, y>0, z == 1, y>0)
418
sage: list(sorted(assumptions(), lambda x,y:cmp(str(x),str(y))))
419
[x > 0, y > 0, z == 1]
420
sage: forget(x>0, z==1)
421
sage: assumptions()
422
[y > 0]
423
sage: assume(y, 'even', z, 'complex')
424
sage: assumptions()
425
[y > 0, y is even, z is complex]
426
sage: cos(y*pi).simplify()
427
1
428
sage: forget(y,'even')
429
sage: cos(y*pi).simplify()
430
cos(pi*y)
431
sage: assumptions()
432
[y > 0, z is complex]
433
sage: forget()
434
sage: assumptions()
435
[]
436
"""
437
if len(args) == 0:
438
_forget_all()
439
return
440
for x in preprocess_assumptions(args):
441
if isinstance(x, (tuple, list)):
442
forget(*x)
443
else:
444
try:
445
x.forget()
446
except KeyError:
447
raise TypeError, "forget not defined for objects of type '%s'"%type(x)
448
449
def assumptions():
450
"""
451
List all current symbolic assumptions.
452
453
EXAMPLES::
454
455
sage: var('x,y,z, w')
456
(x, y, z, w)
457
sage: forget()
458
sage: assume(x^2+y^2 > 0)
459
sage: assumptions()
460
[x^2 + y^2 > 0]
461
sage: forget(x^2+y^2 > 0)
462
sage: assumptions()
463
[]
464
sage: assume(x > y)
465
sage: assume(z > w)
466
sage: list(sorted(assumptions(), lambda x,y:cmp(str(x),str(y))))
467
[x > y, z > w]
468
sage: forget()
469
sage: assumptions()
470
[]
471
"""
472
return list(_assumptions)
473
474
def _forget_all():
475
"""
476
Forget all symbolic assumptions.
477
478
This is called by ``forget()``.
479
480
EXAMPLES::
481
482
sage: var('x,y')
483
(x, y)
484
sage: assume(x > 0, y < 0)
485
sage: bool(x*y < 0) # means definitely true
486
True
487
sage: bool(x*y > 0) # might not be true
488
False
489
sage: forget() # implicitly calls _forget_all
490
sage: bool(x*y < 0) # might not be true
491
False
492
sage: bool(x*y > 0) # might not be true
493
False
494
495
TESTS:
496
497
Check that Trac ticket 7315 is fixed::
498
499
sage: var('m,n')
500
(m, n)
501
sage: assume(n, 'integer'); assume(m, 'integer')
502
sage: sin(n*pi).simplify()
503
0
504
sage: sin(m*pi).simplify()
505
0
506
sage: forget()
507
sage: sin(n*pi).simplify()
508
sin(pi*n)
509
sage: sin(m*pi).simplify()
510
sin(pi*m)
511
"""
512
from sage.calculus.calculus import maxima
513
global _assumptions
514
if len(_assumptions) == 0:
515
return
516
#maxima._eval_line('forget([%s]);'%(','.join([x._maxima_init_() for x in _assumptions])))
517
for x in _assumptions[:]: # need to do this because x.forget() removes x from _assumptions
518
x.forget()
519
_assumptions = []
520
521