r"""
Symbolic Minimum and Maximum
Sage provides a symbolic maximum and minimum due to the fact that the Python
builtin max and min are not able to deal with variables as users might expect.
These functions wait to evaluate if there are variables.
Here you can see some differences::
sage: max(x,x^2)
x
sage: max_symbolic(x,x^2)
max(x, x^2)
sage: f(x) = max_symbolic(x,x^2); f(1/2)
1/2
This works as expected for more than two entries::
sage: max(3,5,x)
5
sage: min(3,5,x)
3
sage: max_symbolic(3,5,x)
max(x, 5)
sage: min_symbolic(3,5,x)
min(x, 3)
"""
from sage.symbolic.function import BuiltinFunction
from sage.symbolic.expression import Expression
from sage.symbolic.ring import SR
from __builtin__ import max as builtin_max, min as builtin_min
class MinMax_base(BuiltinFunction):
def eval_helper(self, this_f, builtin_f, initial_val, args):
"""
EXAMPLES::
sage: max_symbolic(3,5,x) # indirect doctest
max(x, 5)
sage: min_symbolic(3,5,x)
min(x, 3)
"""
arg_is_iter = False
if len(args) == 1:
arg_is_iter = True
args = args[0]
symb_args = []
res = initial_val
num_non_symbolic_args = 0
for x in args:
if isinstance(x, Expression):
symb_args.append(x)
else:
num_non_symbolic_args += 1
res = builtin_f(res, x)
if len(symb_args) == 0:
if res is None:
raise ValueError("return None")
return res
if num_non_symbolic_args <= 1 and not arg_is_iter:
return None
if res is not None: symb_args.append(res)
return this_f(*symb_args)
def __call__(self, *args, **kwds):
"""
EXAMPLES::
sage: max_symbolic(3,5,x)
max(x, 5)
sage: max_symbolic(3,5,x, hold=True)
max(3, 5, x)
sage: max_symbolic([3,5,x])
max(x, 5)
::
sage: min_symbolic(3,5,x)
min(x, 3)
sage: min_symbolic(3,5,x, hold=True)
min(3, 5, x)
sage: min_symbolic([3,5,x])
min(x, 3)
TESTS:
We get an exception if no arguments are given::
sage: max_symbolic()
Traceback (most recent call last):
...
ValueError: number of arguments must be > 0
Check if we return None, when the builtin function would::
sage: max_symbolic([None]) is None
True
sage: max_symbolic([None, None]) is None
True
sage: min_symbolic([None]) is None
True
sage: min_symbolic([None, None]) is None
True
Check if a single argument which is not iterable works::
sage: max_symbolic(None)
Traceback (most recent call last):
...
TypeError: 'NoneType' object is not iterable
sage: max_symbolic(5)
Traceback (most recent call last):
...
TypeError: 'sage.rings.integer.Integer' object is not iterable
sage: max_symbolic(x)
Traceback (most recent call last):
...
TypeError: 'sage.symbolic.expression.Expression' object is not iterable
sage: min_symbolic(5)
Traceback (most recent call last):
...
TypeError: 'sage.rings.integer.Integer' object is not iterable
sage: min_symbolic(x)
Traceback (most recent call last):
...
TypeError: 'sage.symbolic.expression.Expression' object is not iterable
"""
if len(args) == 0:
raise ValueError("number of arguments must be > 0")
if len(args) == 1:
try:
args=(SR._force_pyobject(iter(args[0])),)
except TypeError, e:
raise e
try:
return BuiltinFunction.__call__(self, *args, **kwds)
except ValueError, e:
if e.args[0] == "return None":
return None
class MaxSymbolic(MinMax_base):
def __init__(self):
r"""
Symbolic `\max` function.
The Python builtin `\max` function doesn't work as expected when symbolic
expressions are given as arguments. This function delays evaluation
until all symbolic arguments are substituted with values.
EXAMPLES::
sage: max_symbolic(3, x)
max(3, x)
sage: max_symbolic(3, x).subs(x=5)
5
sage: max_symbolic(3, 5, x)
max(x, 5)
sage: max_symbolic([3,5,x])
max(x, 5)
TESTS::
sage: loads(dumps(max_symbolic(x,5)))
max(x, 5)
sage: latex(max_symbolic(x,5))
\max\left(x, 5\right)
"""
BuiltinFunction.__init__(self, 'max', nargs=0, latex_name="\max")
def _eval_(self, *args):
"""
EXAMPLES::
sage: t = max_symbolic(x, 5); t
max(x, 5)
sage: t.subs(x=3) # indirect doctest
5
sage: max_symbolic(5,3)
5
sage: u = max_symbolic(*(range(10)+[x])); u
max(x, 9)
sage: u.subs(x=-1)
9
sage: u.subs(x=10)
10
sage: max_symbolic([0,x])
max(x, 0)
TESTS::
sage: max_symbolic()
Traceback (most recent call last):
...
ValueError: number of arguments must be > 0
"""
return self.eval_helper(max_symbolic, builtin_max, None, args)
def _evalf_(self, *args, **kwds):
"""
EXAMPLES::
sage: t = max_symbolic(sin(x), cos(x))
sage: t.subs(x=1).n(200)
0.84147098480789650665250232163029899962256306079837106567275
sage: var('y')
y
sage: t = max_symbolic(sin(x), cos(x), y)
sage: u = t.subs(x=1); u
max(sin(1), cos(1), y)
sage: u.n()
Traceback (most recent call last):
...
TypeError: cannot evaluate symbolic expression numerically
::
sage: f = max_symbolic(sin(x), cos(x))
sage: r = integral(f, x, 0, 1)
sage: r.n()
0.8739124411567263
"""
return max_symbolic(args)
max_symbolic = MaxSymbolic()
class MinSymbolic(MinMax_base):
def __init__(self):
r"""
Symbolic `\min` function.
The Python builtin `\min` function doesn't work as expected when symbolic
expressions are given as arguments. This function delays evaluation
until all symbolic arguments are substituted with values.
EXAMPLES::
sage: min_symbolic(3, x)
min(3, x)
sage: min_symbolic(3, x).subs(x=5)
3
sage: min_symbolic(3, 5, x)
min(x, 3)
sage: min_symbolic([3,5,x])
min(x, 3)
TESTS::
sage: loads(dumps(min_symbolic(x,5)))
min(x, 5)
sage: latex(min_symbolic(x,5))
\min\left(x, 5\right)
"""
BuiltinFunction.__init__(self, 'min', nargs=0, latex_name="\min")
def _eval_(self, *args):
"""
EXAMPLES::
sage: t = min_symbolic(x, 5); t
min(x, 5)
sage: t.subs(x=3) # indirect doctest
3
sage: min_symbolic(5,3)
3
sage: u = min_symbolic(*(range(10)+[x])); u
min(x, 0)
sage: u.subs(x=-1)
-1
sage: u.subs(x=10)
0
sage: min_symbolic([3,x])
min(x, 3)
TESTS::
sage: min_symbolic()
Traceback (most recent call last):
...
ValueError: number of arguments must be > 0
"""
return self.eval_helper(min_symbolic, builtin_min, float('inf'), args)
def _evalf_(self, *args, **kwds):
"""
EXAMPLES::
sage: t = min_symbolic(sin(x), cos(x))
sage: t.subs(x=1).n(200)
0.54030230586813971740093660744297660373231042061792222767010
sage: var('y')
y
sage: t = min_symbolic(sin(x), cos(x), y)
sage: u = t.subs(x=1); u
min(sin(1), cos(1), y)
sage: u.n()
Traceback (most recent call last):
...
TypeError: cannot evaluate symbolic expression numerically
"""
return min_symbolic(args)
min_symbolic = MinSymbolic()