Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
""" Assumptions
The ``GenericDeclaration`` class provides assumptions about a symbol or function in verbal form. Such assumptions can be made using the :func:`assume` function in this module, which also can take any relation of symbolic expressions as argument. Use :func:`forget` to clear all assumptions. Creating a variable with a specific domain is equivalent with making an assumption about it.
There is only rudimentary support for consistency and satisfiability checking in Sage. Assumptions are used both in Maxima and Pynac to support or refine some computations. In the following we show how to make and query assumptions. Please see the respective modules for more practical examples.
In addition to the global :func:`assumptions` database, :func:`assuming` creates reusable, stackable context managers allowing for temporary updates of the database for evaluation of a (block of) statements.
EXAMPLES:
The default domain of a symbolic variable is the complex plane::
sage: var('x') x sage: x.is_real() False sage: assume(x,'real') sage: x.is_real() True sage: forget() sage: x.is_real() False
Here is the list of acceptable features::
sage: maxima('features') [integer,noninteger,even,odd,rational,irrational,real,imaginary,complex,analytic,increasing,decreasing,oddfun,evenfun,posfun,constant,commutative,lassociative,rassociative,symmetric,antisymmetric,integervalued]
Set positive domain using a relation::
sage: assume(x>0) sage: x.is_positive() True sage: x.is_real() True sage: assumptions() [x > 0]
Assumptions also affect operations that do not use Maxima::
sage: forget() sage: assume(x, 'even') sage: assume(x, 'real') sage: (-1)^x 1 sage: (-gamma(pi))^x gamma(pi)^x sage: binomial(2*x, x).is_integer() True
Assumptions are added and in some cases checked for consistency::
sage: assume(x>0) sage: assume(x<0) Traceback (most recent call last): ... ValueError: Assumption is inconsistent sage: forget() """ from sage.structure.sage_object import SageObject from sage.rings.all import ZZ, QQ, RR, CC from sage.symbolic.ring import is_SymbolicVariable _assumptions = []
class GenericDeclaration(SageObject): """ This class represents generic assumptions, such as a variable being an integer or a function being increasing. It passes such information to Maxima's declare (wrapped in a context so it is able to forget) and to Pynac.
INPUT:
- ``var`` -- the variable about which assumptions are being made
- ``assumption`` -- a string containing a Maxima feature, either user defined or in the list given by ``maxima('features')``
EXAMPLES::
sage: from sage.symbolic.assumptions import GenericDeclaration sage: decl = GenericDeclaration(x, 'integer') sage: decl.assume() sage: sin(x*pi) 0 sage: decl.forget() sage: sin(x*pi) sin(pi*x) sage: sin(x*pi).simplify() sin(pi*x)
Here is the list of acceptable features::
sage: maxima('features') [integer,noninteger,even,odd,rational,irrational,real,imaginary,complex,analytic,increasing,decreasing,oddfun,evenfun,posfun,constant,commutative,lassociative,rassociative,symmetric,antisymmetric,integervalued] """
def __init__(self, var, assumption): """ This class represents generic assumptions, such as a variable being an integer or a function being increasing. It passes such information to maxima's declare (wrapped in a context so it is able to forget).
INPUT:
- ``var`` -- the variable about which assumptions are being made
- ``assumption`` -- a Maxima feature, either user defined or in the list given by ``maxima('features')``
EXAMPLES::
sage: from sage.symbolic.assumptions import GenericDeclaration sage: decl = GenericDeclaration(x, 'integer') sage: decl.assume() sage: sin(x*pi) 0 sage: decl.forget() sage: sin(x*pi) sin(pi*x)
Here is the list of acceptable features::
sage: maxima('features') [integer,noninteger,even,odd,rational,irrational,real,imaginary,complex,analytic,increasing,decreasing,oddfun,evenfun,posfun,constant,commutative,lassociative,rassociative,symmetric,antisymmetric,integervalued] """
def __repr__(self): """ EXAMPLES::
sage: from sage.symbolic.assumptions import GenericDeclaration sage: GenericDeclaration(x, 'foo') x is foo """
def __eq__(self, other): """ Check whether ``self`` and ``other`` are equal.
TESTS::
sage: from sage.symbolic.assumptions import GenericDeclaration as GDecl sage: var('y') y sage: GDecl(x, 'integer') == GDecl(x, 'integer') True sage: GDecl(x, 'integer') == GDecl(x, 'rational') False sage: GDecl(x, 'integer') == GDecl(y, 'integer') False """ self._assumption == other._assumption)
def __ne__(self, other): """ Check whether ``self`` and ``other`` are not equal.
TESTS::
sage: from sage.symbolic.assumptions import GenericDeclaration as GDecl sage: var('y') y sage: GDecl(x, 'integer') != GDecl(x, 'integer') False sage: GDecl(x, 'integer') != GDecl(x, 'rational') True sage: GDecl(x, 'integer') != GDecl(y, 'integer') True """
def has(self, arg): """ Check if this assumption contains the argument ``arg``.
EXAMPLES::
sage: from sage.symbolic.assumptions import GenericDeclaration as GDecl sage: var('y') y sage: d = GDecl(x, 'integer') sage: d.has(x) True sage: d.has(y) False """
def assume(self): """ Make this assumption.
TESTS::
sage: from sage.symbolic.assumptions import GenericDeclaration sage: decl = GenericDeclaration(x, 'even') sage: decl.assume() sage: cos(x*pi).simplify() 1 sage: decl2 = GenericDeclaration(x, 'odd') sage: decl2.assume() Traceback (most recent call last): ... ValueError: Assumption is inconsistent sage: decl.forget() """ # We get the list here because features may be added with time. raise ValueError("%s not a valid assumption, must be one of %s" % (self._assumption, valid_features)) else: raise
def forget(self): """ Forget this assumption.
TESTS::
sage: from sage.symbolic.assumptions import GenericDeclaration sage: decl = GenericDeclaration(x, 'odd') sage: decl.assume() sage: cos(pi*x) cos(pi*x) sage: cos(pi*x).simplify() -1 sage: decl.forget() sage: cos(x*pi).simplify() cos(pi*x) """ except ValueError: return else: # trying to forget a declaration explicitly rather than implicitly
def contradicts(self, soln): """ Return ``True`` if this assumption is violated by the given variable assignment(s).
INPUT:
- ``soln`` -- Either a dictionary with variables as keys or a symbolic relation with a variable on the left hand side.
EXAMPLES::
sage: from sage.symbolic.assumptions import GenericDeclaration sage: GenericDeclaration(x, 'integer').contradicts(x==4) False sage: GenericDeclaration(x, 'integer').contradicts(x==4.0) False sage: GenericDeclaration(x, 'integer').contradicts(x==4.5) True sage: GenericDeclaration(x, 'integer').contradicts(x==sqrt(17)) True sage: GenericDeclaration(x, 'noninteger').contradicts(x==sqrt(17)) False sage: GenericDeclaration(x, 'noninteger').contradicts(x==17) True sage: GenericDeclaration(x, 'even').contradicts(x==3) True sage: GenericDeclaration(x, 'complex').contradicts(x==3) False sage: GenericDeclaration(x, 'imaginary').contradicts(x==3) True sage: GenericDeclaration(x, 'imaginary').contradicts(x==I) False
sage: var('y,z') (y, z) sage: GenericDeclaration(x, 'imaginary').contradicts(x==y+z) False
sage: GenericDeclaration(x, 'rational').contradicts(y==pi) False sage: GenericDeclaration(x, 'rational').contradicts(x==pi) True sage: GenericDeclaration(x, 'irrational').contradicts(x!=pi) False sage: GenericDeclaration(x, 'rational').contradicts({x: pi, y: pi}) True sage: GenericDeclaration(x, 'rational').contradicts({z: pi, y: pi}) False """ else: return value not in ZZ or ZZ(value) % 2 != 1 return value not in RR
def preprocess_assumptions(args): """ Turn a list of the form ``(var1, var2, ..., 'property')`` into a sequence of declarations ``(var1 is property), (var2 is property), ...``
EXAMPLES::
sage: from sage.symbolic.assumptions import preprocess_assumptions sage: preprocess_assumptions([x, 'integer', x > 4]) [x is integer, x > 4] sage: var('x, y') (x, y) sage: preprocess_assumptions([x, y, 'integer', x > 4, y, 'even']) [x is integer, y is integer, x > 4, y is even] """ and last is not None): else:
def assume(*args): """ Make the given assumptions.
INPUT:
- ``*args`` -- assumptions
EXAMPLES:
Assumptions are typically used to ensure certain relations are evaluated as true that are not true in general.
Here, we verify that for `x>0`, `\sqrt{x^2}=x`::
sage: assume(x > 0) sage: bool(sqrt(x^2) == x) True
This will be assumed in the current Sage session until forgotten::
sage: forget() sage: bool(sqrt(x^2) == x) False
Another major use case is in taking certain integrals and limits where the answers may depend on some sign condition::
sage: var('x, n') (x, n) sage: assume(n+1>0) sage: integral(x^n,x) x^(n + 1)/(n + 1) sage: forget()
::
sage: var('q, a, k') (q, a, k) sage: assume(q > 1) sage: sum(a*q^k, k, 0, oo) Traceback (most recent call last): ... ValueError: Sum is divergent. sage: forget() sage: assume(abs(q) < 1) sage: sum(a*q^k, k, 0, oo) -a/(q - 1) sage: forget()
An integer constraint::
sage: var('n, P, r, r2') (n, P, r, r2) sage: assume(n, 'integer') sage: c = P*e^(r*n) sage: d = P*(1+r2)^n sage: solve(c==d,r2) [r2 == e^r - 1]
Simplifying certain well-known identities works as well::
sage: sin(n*pi) 0 sage: forget() sage: sin(n*pi).simplify() sin(pi*n)
If you make inconsistent or meaningless assumptions, Sage will let you know::
sage: assume(x<0) sage: assume(x>0) Traceback (most recent call last): ... ValueError: Assumption is inconsistent sage: assume(x<1) Traceback (most recent call last): ... ValueError: Assumption is redundant sage: assumptions() [x < 0] sage: forget() sage: assume(x,'even') sage: assume(x,'odd') Traceback (most recent call last): ... ValueError: Assumption is inconsistent sage: forget()
You can also use assumptions to evaluate simple truth values::
sage: x, y, z = var('x, y, z') sage: assume(x>=y,y>=z,z>=x) sage: bool(x==z) True sage: bool(z<x) False sage: bool(z>y) False sage: bool(y==z) True sage: forget() sage: assume(x>=1,x<=1) sage: bool(x==1) True sage: bool(x>1) False sage: forget()
TESTS:
Test that you can do two non-relational declarations at once (fixing :trac:`7084`)::
sage: var('m,n') (m, n) sage: assume(n, 'integer'); assume(m, 'integer') sage: sin(n*pi).simplify() 0 sage: sin(m*pi).simplify() 0 sage: forget() sage: sin(n*pi).simplify() sin(pi*n) sage: sin(m*pi).simplify() sin(pi*m)
Check that positive integers can be created (:trac:`20132`)
sage: forget() sage: x = SR.var('x', domain='positive') sage: assume(x, 'integer') sage: x.is_positive() and x.is_integer() True
sage: forget() sage: x = SR.var('x', domain='integer') sage: assume(x > 0) sage: x.is_positive() and x.is_integer() True
sage: forget() sage: assume(x, "integer") sage: assume(x > 0) sage: x.is_positive() and x.is_integer() True """ else: raise TypeError("assume not defined for objects of type '%s'"%type(x))
def forget(*args): """ Forget the given assumption, or call with no arguments to forget all assumptions.
Here an assumption is some sort of symbolic constraint.
INPUT:
- ``*args`` -- assumptions (default: forget all assumptions)
EXAMPLES:
We define and forget multiple assumptions::
sage: forget() sage: var('x,y,z') (x, y, z) sage: assume(x>0, y>0, z == 1, y>0) sage: sorted(assumptions(), key=lambda x:str(x)) [x > 0, y > 0, z == 1] sage: forget(x>0, z==1) sage: assumptions() [y > 0] sage: assume(y, 'even', z, 'complex') sage: assumptions() [y > 0, y is even, z is complex] sage: cos(y*pi).simplify() 1 sage: forget(y,'even') sage: cos(y*pi).simplify() cos(pi*y) sage: assumptions() [y > 0, z is complex] sage: forget() sage: assumptions() [] """ else: except KeyError: raise TypeError("forget not defined for objects of type '%s'"%type(x))
def assumptions(*args): """ List all current symbolic assumptions.
INPUT:
- ``args`` -- list of variables which can be empty.
OUTPUT:
- list of assumptions on variables. If args is empty it returns all assumptions
EXAMPLES::
sage: var('x, y, z, w') (x, y, z, w) sage: forget() sage: assume(x^2+y^2 > 0) sage: assumptions() [x^2 + y^2 > 0] sage: forget(x^2+y^2 > 0) sage: assumptions() [] sage: assume(x > y) sage: assume(z > w) sage: sorted(assumptions(), key=lambda x: str(x)) [x > y, z > w] sage: forget() sage: assumptions() []
It is also possible to query for assumptions on a variable independently::
sage: x, y, z = var('x y z') sage: assume(x, 'integer') sage: assume(y > 0) sage: assume(y**2 + z**2 == 1) sage: assume(x < 0) sage: assumptions() [x is integer, y > 0, y^2 + z^2 == 1, x < 0] sage: assumptions(x) [x is integer, x < 0] sage: assumptions(x, y) [x is integer, x < 0, y > 0, y^2 + z^2 == 1] sage: assumptions(z) [y^2 + z^2 == 1] """
if statement.has(args[0])]) else: if str(v) in str(statement) ]
def _forget_all(): """ Forget all symbolic assumptions.
This is called by ``forget()``.
EXAMPLES::
sage: forget() sage: var('x,y') (x, y) sage: assume(x > 0, y < 0) sage: bool(x*y < 0) # means definitely true True sage: bool(x*y > 0) # might not be true False sage: forget() # implicitly calls _forget_all sage: bool(x*y < 0) # might not be true False sage: bool(x*y > 0) # might not be true False
TESTS:
Check that :trac:`7315` is fixed::
sage: var('m,n') (m, n) sage: assume(n, 'integer'); assume(m, 'integer') sage: sin(n*pi).simplify() 0 sage: sin(m*pi).simplify() 0 sage: forget() sage: sin(n*pi).simplify() sin(pi*n) sage: sin(m*pi).simplify() sin(pi*m) """ global _assumptions #maxima._eval_line('forget([%s]);'%(','.join([x._maxima_init_() for x in _assumptions])))
class assuming: """ Temporarily modify assumptions.
Create a context manager in which temporary assumptions are added (or substituted) to the current assumptions set.
The set of possible assumptions and declarations is the same as for :func:`assume`.
This can be useful in interactive mode to discover the assumptions necessary to a given integration, or the exact solution to a system of equations.
It can also be used to explore the branches of a :func:`cases()` expression.
As with :func:`assume`, it is an error to add an assumption either redundant or inconsistent with the current assumption set (unless ``replace=True`` is used). See examples.
INPUT:
- ``*args`` -- assumptions (same format as for :func:`assume`).
- ``replace`` -- a boolean (default : ``False``). Specifies whether the new assumptions are added to (default) or replace (if ``replace=True``) the current assumption set.
OUTPUT:
A context manager useable in a ``with`` statement (see examples).
EXAMPLES:
Basic functionality : inside a :func:`with assuming:` block, Sage uses the updated assumptions database. After exit, the original database is restored. ::
sage: var("x") x sage: forget(assumptions()) sage: solve(x^2 == 4,x) [x == -2, x == 2] sage: with assuming(x > 0): ....: solve(x^2 == 4,x) ....: [x == 2] sage: assumptions() []
The local assumptions can be stacked. We can use this functionality to discover incrementally the assumptions necessary to a given calculation (and by the way, to check that Sage's default integrator (Maxima's, that is), sometimes nitpicks for naught). ::
sage: var("y,k,theta") (y, k, theta) sage: dgamma(y,k,theta)=y^(k-1)*e^(-y/theta)/(theta^k*gamma(k)) sage: integrate(dgamma(y,k,theta),y,0,oo) Traceback (most recent call last): ... ValueError: Computation failed since Maxima requested additional constraints; using the 'assume' command before evaluation *may* help (example of legal syntax is 'assume(theta>0)', see `assume?` for more details) Is theta positive or negative? sage: a1=assuming(theta>0) sage: with a1:integrate(dgamma(y,k,theta),y,0,oo) Traceback (most recent call last): ... ValueError: Computation failed since Maxima requested additional constraints; using the 'assume' command before evaluation *may* help (example of legal syntax is 'assume(k>0)', see `assume?` for more details) Is k positive, negative or zero? sage: a2=assuming(k>0) sage: with a1,a2:integrate(dgamma(y,k,theta),y,0,oo) Traceback (most recent call last): ... ValueError: Computation failed since Maxima requested additional constraints; using the 'assume' command before evaluation *may* help (example of legal syntax is 'assume(k>0)', see `assume?` for more details) Is k an integer? sage: a3=assuming(k,"noninteger") sage: with a1,a2,a3:integrate(dgamma(y,k,theta),y,0,oo) 1 sage: a4=assuming(k,"integer") sage: with a1,a2,a4:integrate(dgamma(y,k,theta),y,0,oo) 1
As mentioned above, it is an error to try to introduce redundant or inconsistent assumptions. ::
sage: assume(x > 0) sage: with assuming(x > -1): "I won't see this" Traceback (most recent call last): ... ValueError: Assumption is redundant
sage: with assuming(x < -1): "I won't see this" Traceback (most recent call last): ... ValueError: Assumption is inconsistent
""" def __init__(self,*args, **kwds): """ EXAMPLES::
sage: forget() sage: foo=assuming(x>0) sage: foo.Ass (x > 0,) sage: bool(x>-1) False
"""
def __enter__(self): """ EXAMPLES::
sage: forget() sage: foo=assuming(x>0) sage: bool(x>-1) False sage: foo.__enter__() sage: bool(x>-1) True sage: foo.__exit__() sage: bool(x>-1) False
""" self.OldAss=assumptions() forget(assumptions())
def __exit__(self, *args, **kwds): """ EXAMPLES::
sage: forget() sage: foo=assuming(x>0) sage: bool(x>-1) False sage: foo.__enter__() sage: bool(x>-1) True sage: foo.__exit__() sage: bool(x>-1) False sage: forget()
""" forget(assumptions()) assume(self.OldAss) else: |