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