图片解析应用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

452 lines
14 KiB

  1. """ The module contains implemented functions for interval arithmetic."""
  2. from functools import reduce
  3. from sympy.plotting.intervalmath import interval
  4. from sympy.external import import_module
  5. def Abs(x):
  6. if isinstance(x, (int, float)):
  7. return interval(abs(x))
  8. elif isinstance(x, interval):
  9. if x.start < 0 and x.end > 0:
  10. return interval(0, max(abs(x.start), abs(x.end)), is_valid=x.is_valid)
  11. else:
  12. return interval(abs(x.start), abs(x.end))
  13. else:
  14. raise NotImplementedError
  15. #Monotonic
  16. def exp(x):
  17. """evaluates the exponential of an interval"""
  18. np = import_module('numpy')
  19. if isinstance(x, (int, float)):
  20. return interval(np.exp(x), np.exp(x))
  21. elif isinstance(x, interval):
  22. return interval(np.exp(x.start), np.exp(x.end), is_valid=x.is_valid)
  23. else:
  24. raise NotImplementedError
  25. #Monotonic
  26. def log(x):
  27. """evaluates the natural logarithm of an interval"""
  28. np = import_module('numpy')
  29. if isinstance(x, (int, float)):
  30. if x <= 0:
  31. return interval(-np.inf, np.inf, is_valid=False)
  32. else:
  33. return interval(np.log(x))
  34. elif isinstance(x, interval):
  35. if not x.is_valid:
  36. return interval(-np.inf, np.inf, is_valid=x.is_valid)
  37. elif x.end <= 0:
  38. return interval(-np.inf, np.inf, is_valid=False)
  39. elif x.start <= 0:
  40. return interval(-np.inf, np.inf, is_valid=None)
  41. return interval(np.log(x.start), np.log(x.end))
  42. else:
  43. raise NotImplementedError
  44. #Monotonic
  45. def log10(x):
  46. """evaluates the logarithm to the base 10 of an interval"""
  47. np = import_module('numpy')
  48. if isinstance(x, (int, float)):
  49. if x <= 0:
  50. return interval(-np.inf, np.inf, is_valid=False)
  51. else:
  52. return interval(np.log10(x))
  53. elif isinstance(x, interval):
  54. if not x.is_valid:
  55. return interval(-np.inf, np.inf, is_valid=x.is_valid)
  56. elif x.end <= 0:
  57. return interval(-np.inf, np.inf, is_valid=False)
  58. elif x.start <= 0:
  59. return interval(-np.inf, np.inf, is_valid=None)
  60. return interval(np.log10(x.start), np.log10(x.end))
  61. else:
  62. raise NotImplementedError
  63. #Monotonic
  64. def atan(x):
  65. """evaluates the tan inverse of an interval"""
  66. np = import_module('numpy')
  67. if isinstance(x, (int, float)):
  68. return interval(np.arctan(x))
  69. elif isinstance(x, interval):
  70. start = np.arctan(x.start)
  71. end = np.arctan(x.end)
  72. return interval(start, end, is_valid=x.is_valid)
  73. else:
  74. raise NotImplementedError
  75. #periodic
  76. def sin(x):
  77. """evaluates the sine of an interval"""
  78. np = import_module('numpy')
  79. if isinstance(x, (int, float)):
  80. return interval(np.sin(x))
  81. elif isinstance(x, interval):
  82. if not x.is_valid:
  83. return interval(-1, 1, is_valid=x.is_valid)
  84. na, __ = divmod(x.start, np.pi / 2.0)
  85. nb, __ = divmod(x.end, np.pi / 2.0)
  86. start = min(np.sin(x.start), np.sin(x.end))
  87. end = max(np.sin(x.start), np.sin(x.end))
  88. if nb - na > 4:
  89. return interval(-1, 1, is_valid=x.is_valid)
  90. elif na == nb:
  91. return interval(start, end, is_valid=x.is_valid)
  92. else:
  93. if (na - 1) // 4 != (nb - 1) // 4:
  94. #sin has max
  95. end = 1
  96. if (na - 3) // 4 != (nb - 3) // 4:
  97. #sin has min
  98. start = -1
  99. return interval(start, end)
  100. else:
  101. raise NotImplementedError
  102. #periodic
  103. def cos(x):
  104. """Evaluates the cos of an interval"""
  105. np = import_module('numpy')
  106. if isinstance(x, (int, float)):
  107. return interval(np.sin(x))
  108. elif isinstance(x, interval):
  109. if not (np.isfinite(x.start) and np.isfinite(x.end)):
  110. return interval(-1, 1, is_valid=x.is_valid)
  111. na, __ = divmod(x.start, np.pi / 2.0)
  112. nb, __ = divmod(x.end, np.pi / 2.0)
  113. start = min(np.cos(x.start), np.cos(x.end))
  114. end = max(np.cos(x.start), np.cos(x.end))
  115. if nb - na > 4:
  116. #differ more than 2*pi
  117. return interval(-1, 1, is_valid=x.is_valid)
  118. elif na == nb:
  119. #in the same quadarant
  120. return interval(start, end, is_valid=x.is_valid)
  121. else:
  122. if (na) // 4 != (nb) // 4:
  123. #cos has max
  124. end = 1
  125. if (na - 2) // 4 != (nb - 2) // 4:
  126. #cos has min
  127. start = -1
  128. return interval(start, end, is_valid=x.is_valid)
  129. else:
  130. raise NotImplementedError
  131. def tan(x):
  132. """Evaluates the tan of an interval"""
  133. return sin(x) / cos(x)
  134. #Monotonic
  135. def sqrt(x):
  136. """Evaluates the square root of an interval"""
  137. np = import_module('numpy')
  138. if isinstance(x, (int, float)):
  139. if x > 0:
  140. return interval(np.sqrt(x))
  141. else:
  142. return interval(-np.inf, np.inf, is_valid=False)
  143. elif isinstance(x, interval):
  144. #Outside the domain
  145. if x.end < 0:
  146. return interval(-np.inf, np.inf, is_valid=False)
  147. #Partially outside the domain
  148. elif x.start < 0:
  149. return interval(-np.inf, np.inf, is_valid=None)
  150. else:
  151. return interval(np.sqrt(x.start), np.sqrt(x.end),
  152. is_valid=x.is_valid)
  153. else:
  154. raise NotImplementedError
  155. def imin(*args):
  156. """Evaluates the minimum of a list of intervals"""
  157. np = import_module('numpy')
  158. if not all(isinstance(arg, (int, float, interval)) for arg in args):
  159. return NotImplementedError
  160. else:
  161. new_args = [a for a in args if isinstance(a, (int, float))
  162. or a.is_valid]
  163. if len(new_args) == 0:
  164. if all(a.is_valid is False for a in args):
  165. return interval(-np.inf, np.inf, is_valid=False)
  166. else:
  167. return interval(-np.inf, np.inf, is_valid=None)
  168. start_array = [a if isinstance(a, (int, float)) else a.start
  169. for a in new_args]
  170. end_array = [a if isinstance(a, (int, float)) else a.end
  171. for a in new_args]
  172. return interval(min(start_array), min(end_array))
  173. def imax(*args):
  174. """Evaluates the maximum of a list of intervals"""
  175. np = import_module('numpy')
  176. if not all(isinstance(arg, (int, float, interval)) for arg in args):
  177. return NotImplementedError
  178. else:
  179. new_args = [a for a in args if isinstance(a, (int, float))
  180. or a.is_valid]
  181. if len(new_args) == 0:
  182. if all(a.is_valid is False for a in args):
  183. return interval(-np.inf, np.inf, is_valid=False)
  184. else:
  185. return interval(-np.inf, np.inf, is_valid=None)
  186. start_array = [a if isinstance(a, (int, float)) else a.start
  187. for a in new_args]
  188. end_array = [a if isinstance(a, (int, float)) else a.end
  189. for a in new_args]
  190. return interval(max(start_array), max(end_array))
  191. #Monotonic
  192. def sinh(x):
  193. """Evaluates the hyperbolic sine of an interval"""
  194. np = import_module('numpy')
  195. if isinstance(x, (int, float)):
  196. return interval(np.sinh(x), np.sinh(x))
  197. elif isinstance(x, interval):
  198. return interval(np.sinh(x.start), np.sinh(x.end), is_valid=x.is_valid)
  199. else:
  200. raise NotImplementedError
  201. def cosh(x):
  202. """Evaluates the hyperbolic cos of an interval"""
  203. np = import_module('numpy')
  204. if isinstance(x, (int, float)):
  205. return interval(np.cosh(x), np.cosh(x))
  206. elif isinstance(x, interval):
  207. #both signs
  208. if x.start < 0 and x.end > 0:
  209. end = max(np.cosh(x.start), np.cosh(x.end))
  210. return interval(1, end, is_valid=x.is_valid)
  211. else:
  212. #Monotonic
  213. start = np.cosh(x.start)
  214. end = np.cosh(x.end)
  215. return interval(start, end, is_valid=x.is_valid)
  216. else:
  217. raise NotImplementedError
  218. #Monotonic
  219. def tanh(x):
  220. """Evaluates the hyperbolic tan of an interval"""
  221. np = import_module('numpy')
  222. if isinstance(x, (int, float)):
  223. return interval(np.tanh(x), np.tanh(x))
  224. elif isinstance(x, interval):
  225. return interval(np.tanh(x.start), np.tanh(x.end), is_valid=x.is_valid)
  226. else:
  227. raise NotImplementedError
  228. def asin(x):
  229. """Evaluates the inverse sine of an interval"""
  230. np = import_module('numpy')
  231. if isinstance(x, (int, float)):
  232. #Outside the domain
  233. if abs(x) > 1:
  234. return interval(-np.inf, np.inf, is_valid=False)
  235. else:
  236. return interval(np.arcsin(x), np.arcsin(x))
  237. elif isinstance(x, interval):
  238. #Outside the domain
  239. if x.is_valid is False or x.start > 1 or x.end < -1:
  240. return interval(-np.inf, np.inf, is_valid=False)
  241. #Partially outside the domain
  242. elif x.start < -1 or x.end > 1:
  243. return interval(-np.inf, np.inf, is_valid=None)
  244. else:
  245. start = np.arcsin(x.start)
  246. end = np.arcsin(x.end)
  247. return interval(start, end, is_valid=x.is_valid)
  248. def acos(x):
  249. """Evaluates the inverse cos of an interval"""
  250. np = import_module('numpy')
  251. if isinstance(x, (int, float)):
  252. if abs(x) > 1:
  253. #Outside the domain
  254. return interval(-np.inf, np.inf, is_valid=False)
  255. else:
  256. return interval(np.arccos(x), np.arccos(x))
  257. elif isinstance(x, interval):
  258. #Outside the domain
  259. if x.is_valid is False or x.start > 1 or x.end < -1:
  260. return interval(-np.inf, np.inf, is_valid=False)
  261. #Partially outside the domain
  262. elif x.start < -1 or x.end > 1:
  263. return interval(-np.inf, np.inf, is_valid=None)
  264. else:
  265. start = np.arccos(x.start)
  266. end = np.arccos(x.end)
  267. return interval(start, end, is_valid=x.is_valid)
  268. def ceil(x):
  269. """Evaluates the ceiling of an interval"""
  270. np = import_module('numpy')
  271. if isinstance(x, (int, float)):
  272. return interval(np.ceil(x))
  273. elif isinstance(x, interval):
  274. if x.is_valid is False:
  275. return interval(-np.inf, np.inf, is_valid=False)
  276. else:
  277. start = np.ceil(x.start)
  278. end = np.ceil(x.end)
  279. #Continuous over the interval
  280. if start == end:
  281. return interval(start, end, is_valid=x.is_valid)
  282. else:
  283. #Not continuous over the interval
  284. return interval(start, end, is_valid=None)
  285. else:
  286. return NotImplementedError
  287. def floor(x):
  288. """Evaluates the floor of an interval"""
  289. np = import_module('numpy')
  290. if isinstance(x, (int, float)):
  291. return interval(np.floor(x))
  292. elif isinstance(x, interval):
  293. if x.is_valid is False:
  294. return interval(-np.inf, np.inf, is_valid=False)
  295. else:
  296. start = np.floor(x.start)
  297. end = np.floor(x.end)
  298. #continuous over the argument
  299. if start == end:
  300. return interval(start, end, is_valid=x.is_valid)
  301. else:
  302. #not continuous over the interval
  303. return interval(start, end, is_valid=None)
  304. else:
  305. return NotImplementedError
  306. def acosh(x):
  307. """Evaluates the inverse hyperbolic cosine of an interval"""
  308. np = import_module('numpy')
  309. if isinstance(x, (int, float)):
  310. #Outside the domain
  311. if x < 1:
  312. return interval(-np.inf, np.inf, is_valid=False)
  313. else:
  314. return interval(np.arccosh(x))
  315. elif isinstance(x, interval):
  316. #Outside the domain
  317. if x.end < 1:
  318. return interval(-np.inf, np.inf, is_valid=False)
  319. #Partly outside the domain
  320. elif x.start < 1:
  321. return interval(-np.inf, np.inf, is_valid=None)
  322. else:
  323. start = np.arccosh(x.start)
  324. end = np.arccosh(x.end)
  325. return interval(start, end, is_valid=x.is_valid)
  326. else:
  327. return NotImplementedError
  328. #Monotonic
  329. def asinh(x):
  330. """Evaluates the inverse hyperbolic sine of an interval"""
  331. np = import_module('numpy')
  332. if isinstance(x, (int, float)):
  333. return interval(np.arcsinh(x))
  334. elif isinstance(x, interval):
  335. start = np.arcsinh(x.start)
  336. end = np.arcsinh(x.end)
  337. return interval(start, end, is_valid=x.is_valid)
  338. else:
  339. return NotImplementedError
  340. def atanh(x):
  341. """Evaluates the inverse hyperbolic tangent of an interval"""
  342. np = import_module('numpy')
  343. if isinstance(x, (int, float)):
  344. #Outside the domain
  345. if abs(x) >= 1:
  346. return interval(-np.inf, np.inf, is_valid=False)
  347. else:
  348. return interval(np.arctanh(x))
  349. elif isinstance(x, interval):
  350. #outside the domain
  351. if x.is_valid is False or x.start >= 1 or x.end <= -1:
  352. return interval(-np.inf, np.inf, is_valid=False)
  353. #partly outside the domain
  354. elif x.start <= -1 or x.end >= 1:
  355. return interval(-np.inf, np.inf, is_valid=None)
  356. else:
  357. start = np.arctanh(x.start)
  358. end = np.arctanh(x.end)
  359. return interval(start, end, is_valid=x.is_valid)
  360. else:
  361. return NotImplementedError
  362. #Three valued logic for interval plotting.
  363. def And(*args):
  364. """Defines the three valued ``And`` behaviour for a 2-tuple of
  365. three valued logic values"""
  366. def reduce_and(cmp_intervala, cmp_intervalb):
  367. if cmp_intervala[0] is False or cmp_intervalb[0] is False:
  368. first = False
  369. elif cmp_intervala[0] is None or cmp_intervalb[0] is None:
  370. first = None
  371. else:
  372. first = True
  373. if cmp_intervala[1] is False or cmp_intervalb[1] is False:
  374. second = False
  375. elif cmp_intervala[1] is None or cmp_intervalb[1] is None:
  376. second = None
  377. else:
  378. second = True
  379. return (first, second)
  380. return reduce(reduce_and, args)
  381. def Or(*args):
  382. """Defines the three valued ``Or`` behaviour for a 2-tuple of
  383. three valued logic values"""
  384. def reduce_or(cmp_intervala, cmp_intervalb):
  385. if cmp_intervala[0] is True or cmp_intervalb[0] is True:
  386. first = True
  387. elif cmp_intervala[0] is None or cmp_intervalb[0] is None:
  388. first = None
  389. else:
  390. first = False
  391. if cmp_intervala[1] is True or cmp_intervalb[1] is True:
  392. second = True
  393. elif cmp_intervala[1] is None or cmp_intervalb[1] is None:
  394. second = None
  395. else:
  396. second = False
  397. return (first, second)
  398. return reduce(reduce_or, args)