Spaces:
Sleeping
Sleeping
""" The module contains implemented functions for interval arithmetic.""" | |
from functools import reduce | |
from sympy.plotting.intervalmath import interval | |
from sympy.external import import_module | |
def Abs(x): | |
if isinstance(x, (int, float)): | |
return interval(abs(x)) | |
elif isinstance(x, interval): | |
if x.start < 0 and x.end > 0: | |
return interval(0, max(abs(x.start), abs(x.end)), is_valid=x.is_valid) | |
else: | |
return interval(abs(x.start), abs(x.end)) | |
else: | |
raise NotImplementedError | |
#Monotonic | |
def exp(x): | |
"""evaluates the exponential of an interval""" | |
np = import_module('numpy') | |
if isinstance(x, (int, float)): | |
return interval(np.exp(x), np.exp(x)) | |
elif isinstance(x, interval): | |
return interval(np.exp(x.start), np.exp(x.end), is_valid=x.is_valid) | |
else: | |
raise NotImplementedError | |
#Monotonic | |
def log(x): | |
"""evaluates the natural logarithm of an interval""" | |
np = import_module('numpy') | |
if isinstance(x, (int, float)): | |
if x <= 0: | |
return interval(-np.inf, np.inf, is_valid=False) | |
else: | |
return interval(np.log(x)) | |
elif isinstance(x, interval): | |
if not x.is_valid: | |
return interval(-np.inf, np.inf, is_valid=x.is_valid) | |
elif x.end <= 0: | |
return interval(-np.inf, np.inf, is_valid=False) | |
elif x.start <= 0: | |
return interval(-np.inf, np.inf, is_valid=None) | |
return interval(np.log(x.start), np.log(x.end)) | |
else: | |
raise NotImplementedError | |
#Monotonic | |
def log10(x): | |
"""evaluates the logarithm to the base 10 of an interval""" | |
np = import_module('numpy') | |
if isinstance(x, (int, float)): | |
if x <= 0: | |
return interval(-np.inf, np.inf, is_valid=False) | |
else: | |
return interval(np.log10(x)) | |
elif isinstance(x, interval): | |
if not x.is_valid: | |
return interval(-np.inf, np.inf, is_valid=x.is_valid) | |
elif x.end <= 0: | |
return interval(-np.inf, np.inf, is_valid=False) | |
elif x.start <= 0: | |
return interval(-np.inf, np.inf, is_valid=None) | |
return interval(np.log10(x.start), np.log10(x.end)) | |
else: | |
raise NotImplementedError | |
#Monotonic | |
def atan(x): | |
"""evaluates the tan inverse of an interval""" | |
np = import_module('numpy') | |
if isinstance(x, (int, float)): | |
return interval(np.arctan(x)) | |
elif isinstance(x, interval): | |
start = np.arctan(x.start) | |
end = np.arctan(x.end) | |
return interval(start, end, is_valid=x.is_valid) | |
else: | |
raise NotImplementedError | |
#periodic | |
def sin(x): | |
"""evaluates the sine of an interval""" | |
np = import_module('numpy') | |
if isinstance(x, (int, float)): | |
return interval(np.sin(x)) | |
elif isinstance(x, interval): | |
if not x.is_valid: | |
return interval(-1, 1, is_valid=x.is_valid) | |
na, __ = divmod(x.start, np.pi / 2.0) | |
nb, __ = divmod(x.end, np.pi / 2.0) | |
start = min(np.sin(x.start), np.sin(x.end)) | |
end = max(np.sin(x.start), np.sin(x.end)) | |
if nb - na > 4: | |
return interval(-1, 1, is_valid=x.is_valid) | |
elif na == nb: | |
return interval(start, end, is_valid=x.is_valid) | |
else: | |
if (na - 1) // 4 != (nb - 1) // 4: | |
#sin has max | |
end = 1 | |
if (na - 3) // 4 != (nb - 3) // 4: | |
#sin has min | |
start = -1 | |
return interval(start, end) | |
else: | |
raise NotImplementedError | |
#periodic | |
def cos(x): | |
"""Evaluates the cos of an interval""" | |
np = import_module('numpy') | |
if isinstance(x, (int, float)): | |
return interval(np.sin(x)) | |
elif isinstance(x, interval): | |
if not (np.isfinite(x.start) and np.isfinite(x.end)): | |
return interval(-1, 1, is_valid=x.is_valid) | |
na, __ = divmod(x.start, np.pi / 2.0) | |
nb, __ = divmod(x.end, np.pi / 2.0) | |
start = min(np.cos(x.start), np.cos(x.end)) | |
end = max(np.cos(x.start), np.cos(x.end)) | |
if nb - na > 4: | |
#differ more than 2*pi | |
return interval(-1, 1, is_valid=x.is_valid) | |
elif na == nb: | |
#in the same quadarant | |
return interval(start, end, is_valid=x.is_valid) | |
else: | |
if (na) // 4 != (nb) // 4: | |
#cos has max | |
end = 1 | |
if (na - 2) // 4 != (nb - 2) // 4: | |
#cos has min | |
start = -1 | |
return interval(start, end, is_valid=x.is_valid) | |
else: | |
raise NotImplementedError | |
def tan(x): | |
"""Evaluates the tan of an interval""" | |
return sin(x) / cos(x) | |
#Monotonic | |
def sqrt(x): | |
"""Evaluates the square root of an interval""" | |
np = import_module('numpy') | |
if isinstance(x, (int, float)): | |
if x > 0: | |
return interval(np.sqrt(x)) | |
else: | |
return interval(-np.inf, np.inf, is_valid=False) | |
elif isinstance(x, interval): | |
#Outside the domain | |
if x.end < 0: | |
return interval(-np.inf, np.inf, is_valid=False) | |
#Partially outside the domain | |
elif x.start < 0: | |
return interval(-np.inf, np.inf, is_valid=None) | |
else: | |
return interval(np.sqrt(x.start), np.sqrt(x.end), | |
is_valid=x.is_valid) | |
else: | |
raise NotImplementedError | |
def imin(*args): | |
"""Evaluates the minimum of a list of intervals""" | |
np = import_module('numpy') | |
if not all(isinstance(arg, (int, float, interval)) for arg in args): | |
return NotImplementedError | |
else: | |
new_args = [a for a in args if isinstance(a, (int, float)) | |
or a.is_valid] | |
if len(new_args) == 0: | |
if all(a.is_valid is False for a in args): | |
return interval(-np.inf, np.inf, is_valid=False) | |
else: | |
return interval(-np.inf, np.inf, is_valid=None) | |
start_array = [a if isinstance(a, (int, float)) else a.start | |
for a in new_args] | |
end_array = [a if isinstance(a, (int, float)) else a.end | |
for a in new_args] | |
return interval(min(start_array), min(end_array)) | |
def imax(*args): | |
"""Evaluates the maximum of a list of intervals""" | |
np = import_module('numpy') | |
if not all(isinstance(arg, (int, float, interval)) for arg in args): | |
return NotImplementedError | |
else: | |
new_args = [a for a in args if isinstance(a, (int, float)) | |
or a.is_valid] | |
if len(new_args) == 0: | |
if all(a.is_valid is False for a in args): | |
return interval(-np.inf, np.inf, is_valid=False) | |
else: | |
return interval(-np.inf, np.inf, is_valid=None) | |
start_array = [a if isinstance(a, (int, float)) else a.start | |
for a in new_args] | |
end_array = [a if isinstance(a, (int, float)) else a.end | |
for a in new_args] | |
return interval(max(start_array), max(end_array)) | |
#Monotonic | |
def sinh(x): | |
"""Evaluates the hyperbolic sine of an interval""" | |
np = import_module('numpy') | |
if isinstance(x, (int, float)): | |
return interval(np.sinh(x), np.sinh(x)) | |
elif isinstance(x, interval): | |
return interval(np.sinh(x.start), np.sinh(x.end), is_valid=x.is_valid) | |
else: | |
raise NotImplementedError | |
def cosh(x): | |
"""Evaluates the hyperbolic cos of an interval""" | |
np = import_module('numpy') | |
if isinstance(x, (int, float)): | |
return interval(np.cosh(x), np.cosh(x)) | |
elif isinstance(x, interval): | |
#both signs | |
if x.start < 0 and x.end > 0: | |
end = max(np.cosh(x.start), np.cosh(x.end)) | |
return interval(1, end, is_valid=x.is_valid) | |
else: | |
#Monotonic | |
start = np.cosh(x.start) | |
end = np.cosh(x.end) | |
return interval(start, end, is_valid=x.is_valid) | |
else: | |
raise NotImplementedError | |
#Monotonic | |
def tanh(x): | |
"""Evaluates the hyperbolic tan of an interval""" | |
np = import_module('numpy') | |
if isinstance(x, (int, float)): | |
return interval(np.tanh(x), np.tanh(x)) | |
elif isinstance(x, interval): | |
return interval(np.tanh(x.start), np.tanh(x.end), is_valid=x.is_valid) | |
else: | |
raise NotImplementedError | |
def asin(x): | |
"""Evaluates the inverse sine of an interval""" | |
np = import_module('numpy') | |
if isinstance(x, (int, float)): | |
#Outside the domain | |
if abs(x) > 1: | |
return interval(-np.inf, np.inf, is_valid=False) | |
else: | |
return interval(np.arcsin(x), np.arcsin(x)) | |
elif isinstance(x, interval): | |
#Outside the domain | |
if x.is_valid is False or x.start > 1 or x.end < -1: | |
return interval(-np.inf, np.inf, is_valid=False) | |
#Partially outside the domain | |
elif x.start < -1 or x.end > 1: | |
return interval(-np.inf, np.inf, is_valid=None) | |
else: | |
start = np.arcsin(x.start) | |
end = np.arcsin(x.end) | |
return interval(start, end, is_valid=x.is_valid) | |
def acos(x): | |
"""Evaluates the inverse cos of an interval""" | |
np = import_module('numpy') | |
if isinstance(x, (int, float)): | |
if abs(x) > 1: | |
#Outside the domain | |
return interval(-np.inf, np.inf, is_valid=False) | |
else: | |
return interval(np.arccos(x), np.arccos(x)) | |
elif isinstance(x, interval): | |
#Outside the domain | |
if x.is_valid is False or x.start > 1 or x.end < -1: | |
return interval(-np.inf, np.inf, is_valid=False) | |
#Partially outside the domain | |
elif x.start < -1 or x.end > 1: | |
return interval(-np.inf, np.inf, is_valid=None) | |
else: | |
start = np.arccos(x.start) | |
end = np.arccos(x.end) | |
return interval(start, end, is_valid=x.is_valid) | |
def ceil(x): | |
"""Evaluates the ceiling of an interval""" | |
np = import_module('numpy') | |
if isinstance(x, (int, float)): | |
return interval(np.ceil(x)) | |
elif isinstance(x, interval): | |
if x.is_valid is False: | |
return interval(-np.inf, np.inf, is_valid=False) | |
else: | |
start = np.ceil(x.start) | |
end = np.ceil(x.end) | |
#Continuous over the interval | |
if start == end: | |
return interval(start, end, is_valid=x.is_valid) | |
else: | |
#Not continuous over the interval | |
return interval(start, end, is_valid=None) | |
else: | |
return NotImplementedError | |
def floor(x): | |
"""Evaluates the floor of an interval""" | |
np = import_module('numpy') | |
if isinstance(x, (int, float)): | |
return interval(np.floor(x)) | |
elif isinstance(x, interval): | |
if x.is_valid is False: | |
return interval(-np.inf, np.inf, is_valid=False) | |
else: | |
start = np.floor(x.start) | |
end = np.floor(x.end) | |
#continuous over the argument | |
if start == end: | |
return interval(start, end, is_valid=x.is_valid) | |
else: | |
#not continuous over the interval | |
return interval(start, end, is_valid=None) | |
else: | |
return NotImplementedError | |
def acosh(x): | |
"""Evaluates the inverse hyperbolic cosine of an interval""" | |
np = import_module('numpy') | |
if isinstance(x, (int, float)): | |
#Outside the domain | |
if x < 1: | |
return interval(-np.inf, np.inf, is_valid=False) | |
else: | |
return interval(np.arccosh(x)) | |
elif isinstance(x, interval): | |
#Outside the domain | |
if x.end < 1: | |
return interval(-np.inf, np.inf, is_valid=False) | |
#Partly outside the domain | |
elif x.start < 1: | |
return interval(-np.inf, np.inf, is_valid=None) | |
else: | |
start = np.arccosh(x.start) | |
end = np.arccosh(x.end) | |
return interval(start, end, is_valid=x.is_valid) | |
else: | |
return NotImplementedError | |
#Monotonic | |
def asinh(x): | |
"""Evaluates the inverse hyperbolic sine of an interval""" | |
np = import_module('numpy') | |
if isinstance(x, (int, float)): | |
return interval(np.arcsinh(x)) | |
elif isinstance(x, interval): | |
start = np.arcsinh(x.start) | |
end = np.arcsinh(x.end) | |
return interval(start, end, is_valid=x.is_valid) | |
else: | |
return NotImplementedError | |
def atanh(x): | |
"""Evaluates the inverse hyperbolic tangent of an interval""" | |
np = import_module('numpy') | |
if isinstance(x, (int, float)): | |
#Outside the domain | |
if abs(x) >= 1: | |
return interval(-np.inf, np.inf, is_valid=False) | |
else: | |
return interval(np.arctanh(x)) | |
elif isinstance(x, interval): | |
#outside the domain | |
if x.is_valid is False or x.start >= 1 or x.end <= -1: | |
return interval(-np.inf, np.inf, is_valid=False) | |
#partly outside the domain | |
elif x.start <= -1 or x.end >= 1: | |
return interval(-np.inf, np.inf, is_valid=None) | |
else: | |
start = np.arctanh(x.start) | |
end = np.arctanh(x.end) | |
return interval(start, end, is_valid=x.is_valid) | |
else: | |
return NotImplementedError | |
#Three valued logic for interval plotting. | |
def And(*args): | |
"""Defines the three valued ``And`` behaviour for a 2-tuple of | |
three valued logic values""" | |
def reduce_and(cmp_intervala, cmp_intervalb): | |
if cmp_intervala[0] is False or cmp_intervalb[0] is False: | |
first = False | |
elif cmp_intervala[0] is None or cmp_intervalb[0] is None: | |
first = None | |
else: | |
first = True | |
if cmp_intervala[1] is False or cmp_intervalb[1] is False: | |
second = False | |
elif cmp_intervala[1] is None or cmp_intervalb[1] is None: | |
second = None | |
else: | |
second = True | |
return (first, second) | |
return reduce(reduce_and, args) | |
def Or(*args): | |
"""Defines the three valued ``Or`` behaviour for a 2-tuple of | |
three valued logic values""" | |
def reduce_or(cmp_intervala, cmp_intervalb): | |
if cmp_intervala[0] is True or cmp_intervalb[0] is True: | |
first = True | |
elif cmp_intervala[0] is None or cmp_intervalb[0] is None: | |
first = None | |
else: | |
first = False | |
if cmp_intervala[1] is True or cmp_intervalb[1] is True: | |
second = True | |
elif cmp_intervala[1] is None or cmp_intervalb[1] is None: | |
second = None | |
else: | |
second = False | |
return (first, second) | |
return reduce(reduce_or, args) | |