test_series.py 64 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771
  1. from sympy import (
  2. latex, exp, symbols, I, pi, sin, cos, tan, log, sqrt,
  3. re, im, arg, frac, Sum, S, Abs, lambdify,
  4. Function, dsolve, Eq, floor, Tuple
  5. )
  6. from sympy.external import import_module
  7. from sympy.plotting.series import (
  8. LineOver1DRangeSeries, Parametric2DLineSeries, Parametric3DLineSeries,
  9. SurfaceOver2DRangeSeries, ContourSeries, ParametricSurfaceSeries,
  10. ImplicitSeries, _set_discretization_points, List2DSeries
  11. )
  12. from sympy.testing.pytest import raises, warns, XFAIL, skip, ignore_warnings
  13. np = import_module('numpy')
  14. def test_adaptive():
  15. # verify that adaptive-related keywords produces the expected results
  16. if not np:
  17. skip("numpy not installed.")
  18. x, y = symbols("x, y")
  19. s1 = LineOver1DRangeSeries(sin(x), (x, -10, 10), "", adaptive=True,
  20. depth=2)
  21. x1, _ = s1.get_data()
  22. s2 = LineOver1DRangeSeries(sin(x), (x, -10, 10), "", adaptive=True,
  23. depth=5)
  24. x2, _ = s2.get_data()
  25. s3 = LineOver1DRangeSeries(sin(x), (x, -10, 10), "", adaptive=True)
  26. x3, _ = s3.get_data()
  27. assert len(x1) < len(x2) < len(x3)
  28. s1 = Parametric2DLineSeries(cos(x), sin(x), (x, 0, 2*pi),
  29. adaptive=True, depth=2)
  30. x1, _, _, = s1.get_data()
  31. s2 = Parametric2DLineSeries(cos(x), sin(x), (x, 0, 2*pi),
  32. adaptive=True, depth=5)
  33. x2, _, _ = s2.get_data()
  34. s3 = Parametric2DLineSeries(cos(x), sin(x), (x, 0, 2*pi),
  35. adaptive=True)
  36. x3, _, _ = s3.get_data()
  37. assert len(x1) < len(x2) < len(x3)
  38. def test_detect_poles():
  39. if not np:
  40. skip("numpy not installed.")
  41. x, u = symbols("x, u")
  42. s1 = LineOver1DRangeSeries(tan(x), (x, -pi, pi),
  43. adaptive=False, n=1000, detect_poles=False)
  44. xx1, yy1 = s1.get_data()
  45. s2 = LineOver1DRangeSeries(tan(x), (x, -pi, pi),
  46. adaptive=False, n=1000, detect_poles=True, eps=0.01)
  47. xx2, yy2 = s2.get_data()
  48. # eps is too small: doesn't detect any poles
  49. s3 = LineOver1DRangeSeries(tan(x), (x, -pi, pi),
  50. adaptive=False, n=1000, detect_poles=True, eps=1e-06)
  51. xx3, yy3 = s3.get_data()
  52. s4 = LineOver1DRangeSeries(tan(x), (x, -pi, pi),
  53. adaptive=False, n=1000, detect_poles="symbolic")
  54. xx4, yy4 = s4.get_data()
  55. assert np.allclose(xx1, xx2) and np.allclose(xx1, xx3) and np.allclose(xx1, xx4)
  56. assert not np.any(np.isnan(yy1))
  57. assert not np.any(np.isnan(yy3))
  58. assert np.any(np.isnan(yy2))
  59. assert np.any(np.isnan(yy4))
  60. assert len(s2.poles_locations) == len(s3.poles_locations) == 0
  61. assert len(s4.poles_locations) == 2
  62. assert np.allclose(np.abs(s4.poles_locations), np.pi / 2)
  63. with warns(
  64. UserWarning,
  65. match="NumPy is unable to evaluate with complex numbers some of",
  66. test_stacklevel=False,
  67. ):
  68. s1 = LineOver1DRangeSeries(frac(x), (x, -10, 10),
  69. adaptive=False, n=1000, detect_poles=False)
  70. s2 = LineOver1DRangeSeries(frac(x), (x, -10, 10),
  71. adaptive=False, n=1000, detect_poles=True, eps=0.05)
  72. s3 = LineOver1DRangeSeries(frac(x), (x, -10, 10),
  73. adaptive=False, n=1000, detect_poles="symbolic")
  74. xx1, yy1 = s1.get_data()
  75. xx2, yy2 = s2.get_data()
  76. xx3, yy3 = s3.get_data()
  77. assert np.allclose(xx1, xx2) and np.allclose(xx1, xx3)
  78. assert not np.any(np.isnan(yy1))
  79. assert np.any(np.isnan(yy2)) and np.any(np.isnan(yy2))
  80. assert not np.allclose(yy1, yy2, equal_nan=True)
  81. # The poles below are actually step discontinuities.
  82. assert len(s3.poles_locations) == 21
  83. s1 = LineOver1DRangeSeries(tan(u * x), (x, -pi, pi), params={u: 1},
  84. adaptive=False, n=1000, detect_poles=False)
  85. xx1, yy1 = s1.get_data()
  86. s2 = LineOver1DRangeSeries(tan(u * x), (x, -pi, pi), params={u: 1},
  87. adaptive=False, n=1000, detect_poles=True, eps=0.01)
  88. xx2, yy2 = s2.get_data()
  89. # eps is too small: doesn't detect any poles
  90. s3 = LineOver1DRangeSeries(tan(u * x), (x, -pi, pi), params={u: 1},
  91. adaptive=False, n=1000, detect_poles=True, eps=1e-06)
  92. xx3, yy3 = s3.get_data()
  93. s4 = LineOver1DRangeSeries(tan(u * x), (x, -pi, pi), params={u: 1},
  94. adaptive=False, n=1000, detect_poles="symbolic")
  95. xx4, yy4 = s4.get_data()
  96. assert np.allclose(xx1, xx2) and np.allclose(xx1, xx3) and np.allclose(xx1, xx4)
  97. assert not np.any(np.isnan(yy1))
  98. assert not np.any(np.isnan(yy3))
  99. assert np.any(np.isnan(yy2))
  100. assert np.any(np.isnan(yy4))
  101. assert len(s2.poles_locations) == len(s3.poles_locations) == 0
  102. assert len(s4.poles_locations) == 2
  103. assert np.allclose(np.abs(s4.poles_locations), np.pi / 2)
  104. with warns(
  105. UserWarning,
  106. match="NumPy is unable to evaluate with complex numbers some of",
  107. test_stacklevel=False,
  108. ):
  109. u, v = symbols("u, v", real=True)
  110. n = S(1) / 3
  111. f = (u + I * v)**n
  112. r, i = re(f), im(f)
  113. s1 = Parametric2DLineSeries(r.subs(u, -2), i.subs(u, -2), (v, -2, 2),
  114. adaptive=False, n=1000, detect_poles=False)
  115. s2 = Parametric2DLineSeries(r.subs(u, -2), i.subs(u, -2), (v, -2, 2),
  116. adaptive=False, n=1000, detect_poles=True)
  117. with ignore_warnings(RuntimeWarning):
  118. xx1, yy1, pp1 = s1.get_data()
  119. assert not np.isnan(yy1).any()
  120. xx2, yy2, pp2 = s2.get_data()
  121. assert np.isnan(yy2).any()
  122. with warns(
  123. UserWarning,
  124. match="NumPy is unable to evaluate with complex numbers some of",
  125. test_stacklevel=False,
  126. ):
  127. f = (x * u + x * I * v)**n
  128. r, i = re(f), im(f)
  129. s1 = Parametric2DLineSeries(r.subs(u, -2), i.subs(u, -2),
  130. (v, -2, 2), params={x: 1},
  131. adaptive=False, n1=1000, detect_poles=False)
  132. s2 = Parametric2DLineSeries(r.subs(u, -2), i.subs(u, -2),
  133. (v, -2, 2), params={x: 1},
  134. adaptive=False, n1=1000, detect_poles=True)
  135. with ignore_warnings(RuntimeWarning):
  136. xx1, yy1, pp1 = s1.get_data()
  137. assert not np.isnan(yy1).any()
  138. xx2, yy2, pp2 = s2.get_data()
  139. assert np.isnan(yy2).any()
  140. def test_number_discretization_points():
  141. # verify that the different ways to set the number of discretization
  142. # points are consistent with each other.
  143. if not np:
  144. skip("numpy not installed.")
  145. x, y, z = symbols("x:z")
  146. for pt in [LineOver1DRangeSeries, Parametric2DLineSeries,
  147. Parametric3DLineSeries]:
  148. kw1 = _set_discretization_points({"n": 10}, pt)
  149. kw2 = _set_discretization_points({"n": [10, 20, 30]}, pt)
  150. kw3 = _set_discretization_points({"n1": 10}, pt)
  151. assert all(("n1" in kw) and kw["n1"] == 10 for kw in [kw1, kw2, kw3])
  152. for pt in [SurfaceOver2DRangeSeries, ContourSeries, ParametricSurfaceSeries,
  153. ImplicitSeries]:
  154. kw1 = _set_discretization_points({"n": 10}, pt)
  155. kw2 = _set_discretization_points({"n": [10, 20, 30]}, pt)
  156. kw3 = _set_discretization_points({"n1": 10, "n2": 20}, pt)
  157. assert kw1["n1"] == kw1["n2"] == 10
  158. assert all((kw["n1"] == 10) and (kw["n2"] == 20) for kw in [kw2, kw3])
  159. # verify that line-related series can deal with large float number of
  160. # discretization points
  161. LineOver1DRangeSeries(cos(x), (x, -5, 5), adaptive=False, n=1e04).get_data()
  162. def test_list2dseries():
  163. if not np:
  164. skip("numpy not installed.")
  165. xx = np.linspace(-3, 3, 10)
  166. yy1 = np.cos(xx)
  167. yy2 = np.linspace(-3, 3, 20)
  168. # same number of elements: everything is fine
  169. s = List2DSeries(xx, yy1)
  170. assert not s.is_parametric
  171. # different number of elements: error
  172. raises(ValueError, lambda: List2DSeries(xx, yy2))
  173. # no color func: returns only x, y components and s in not parametric
  174. s = List2DSeries(xx, yy1)
  175. xxs, yys = s.get_data()
  176. assert np.allclose(xx, xxs)
  177. assert np.allclose(yy1, yys)
  178. assert not s.is_parametric
  179. def test_interactive_vs_noninteractive():
  180. # verify that if a *Series class receives a `params` dictionary, it sets
  181. # is_interactive=True
  182. x, y, z, u, v = symbols("x, y, z, u, v")
  183. s = LineOver1DRangeSeries(cos(x), (x, -5, 5))
  184. assert not s.is_interactive
  185. s = LineOver1DRangeSeries(u * cos(x), (x, -5, 5), params={u: 1})
  186. assert s.is_interactive
  187. s = Parametric2DLineSeries(cos(x), sin(x), (x, -5, 5))
  188. assert not s.is_interactive
  189. s = Parametric2DLineSeries(u * cos(x), u * sin(x), (x, -5, 5),
  190. params={u: 1})
  191. assert s.is_interactive
  192. s = Parametric3DLineSeries(cos(x), sin(x), x, (x, -5, 5))
  193. assert not s.is_interactive
  194. s = Parametric3DLineSeries(u * cos(x), u * sin(x), x, (x, -5, 5),
  195. params={u: 1})
  196. assert s.is_interactive
  197. s = SurfaceOver2DRangeSeries(cos(x * y), (x, -5, 5), (y, -5, 5))
  198. assert not s.is_interactive
  199. s = SurfaceOver2DRangeSeries(u * cos(x * y), (x, -5, 5), (y, -5, 5),
  200. params={u: 1})
  201. assert s.is_interactive
  202. s = ContourSeries(cos(x * y), (x, -5, 5), (y, -5, 5))
  203. assert not s.is_interactive
  204. s = ContourSeries(u * cos(x * y), (x, -5, 5), (y, -5, 5),
  205. params={u: 1})
  206. assert s.is_interactive
  207. s = ParametricSurfaceSeries(u * cos(v), v * sin(u), u + v,
  208. (u, -5, 5), (v, -5, 5))
  209. assert not s.is_interactive
  210. s = ParametricSurfaceSeries(u * cos(v * x), v * sin(u), u + v,
  211. (u, -5, 5), (v, -5, 5), params={x: 1})
  212. assert s.is_interactive
  213. def test_lin_log_scale():
  214. # Verify that data series create the correct spacing in the data.
  215. if not np:
  216. skip("numpy not installed.")
  217. x, y, z = symbols("x, y, z")
  218. s = LineOver1DRangeSeries(x, (x, 1, 10), adaptive=False, n=50,
  219. xscale="linear")
  220. xx, _ = s.get_data()
  221. assert np.isclose(xx[1] - xx[0], xx[-1] - xx[-2])
  222. s = LineOver1DRangeSeries(x, (x, 1, 10), adaptive=False, n=50,
  223. xscale="log")
  224. xx, _ = s.get_data()
  225. assert not np.isclose(xx[1] - xx[0], xx[-1] - xx[-2])
  226. s = Parametric2DLineSeries(
  227. cos(x), sin(x), (x, pi / 2, 1.5 * pi), adaptive=False, n=50,
  228. xscale="linear")
  229. _, _, param = s.get_data()
  230. assert np.isclose(param[1] - param[0], param[-1] - param[-2])
  231. s = Parametric2DLineSeries(
  232. cos(x), sin(x), (x, pi / 2, 1.5 * pi), adaptive=False, n=50,
  233. xscale="log")
  234. _, _, param = s.get_data()
  235. assert not np.isclose(param[1] - param[0], param[-1] - param[-2])
  236. s = Parametric3DLineSeries(
  237. cos(x), sin(x), x, (x, pi / 2, 1.5 * pi), adaptive=False, n=50,
  238. xscale="linear")
  239. _, _, _, param = s.get_data()
  240. assert np.isclose(param[1] - param[0], param[-1] - param[-2])
  241. s = Parametric3DLineSeries(
  242. cos(x), sin(x), x, (x, pi / 2, 1.5 * pi), adaptive=False, n=50,
  243. xscale="log")
  244. _, _, _, param = s.get_data()
  245. assert not np.isclose(param[1] - param[0], param[-1] - param[-2])
  246. s = SurfaceOver2DRangeSeries(
  247. cos(x ** 2 + y ** 2), (x, 1, 5), (y, 1, 5), n=10,
  248. xscale="linear", yscale="linear")
  249. xx, yy, _ = s.get_data()
  250. assert np.isclose(xx[0, 1] - xx[0, 0], xx[0, -1] - xx[0, -2])
  251. assert np.isclose(yy[1, 0] - yy[0, 0], yy[-1, 0] - yy[-2, 0])
  252. s = SurfaceOver2DRangeSeries(
  253. cos(x ** 2 + y ** 2), (x, 1, 5), (y, 1, 5), n=10,
  254. xscale="log", yscale="log")
  255. xx, yy, _ = s.get_data()
  256. assert not np.isclose(xx[0, 1] - xx[0, 0], xx[0, -1] - xx[0, -2])
  257. assert not np.isclose(yy[1, 0] - yy[0, 0], yy[-1, 0] - yy[-2, 0])
  258. s = ImplicitSeries(
  259. cos(x ** 2 + y ** 2) > 0, (x, 1, 5), (y, 1, 5),
  260. n1=10, n2=10, xscale="linear", yscale="linear", adaptive=False)
  261. xx, yy, _, _ = s.get_data()
  262. assert np.isclose(xx[0, 1] - xx[0, 0], xx[0, -1] - xx[0, -2])
  263. assert np.isclose(yy[1, 0] - yy[0, 0], yy[-1, 0] - yy[-2, 0])
  264. s = ImplicitSeries(
  265. cos(x ** 2 + y ** 2) > 0, (x, 1, 5), (y, 1, 5),
  266. n=10, xscale="log", yscale="log", adaptive=False)
  267. xx, yy, _, _ = s.get_data()
  268. assert not np.isclose(xx[0, 1] - xx[0, 0], xx[0, -1] - xx[0, -2])
  269. assert not np.isclose(yy[1, 0] - yy[0, 0], yy[-1, 0] - yy[-2, 0])
  270. def test_rendering_kw():
  271. # verify that each series exposes the `rendering_kw` attribute
  272. if not np:
  273. skip("numpy not installed.")
  274. u, v, x, y, z = symbols("u, v, x:z")
  275. s = List2DSeries([1, 2, 3], [4, 5, 6])
  276. assert isinstance(s.rendering_kw, dict)
  277. s = LineOver1DRangeSeries(1, (x, -5, 5))
  278. assert isinstance(s.rendering_kw, dict)
  279. s = Parametric2DLineSeries(sin(x), cos(x), (x, 0, pi))
  280. assert isinstance(s.rendering_kw, dict)
  281. s = Parametric3DLineSeries(cos(x), sin(x), x, (x, 0, 2 * pi))
  282. assert isinstance(s.rendering_kw, dict)
  283. s = SurfaceOver2DRangeSeries(x + y, (x, -2, 2), (y, -3, 3))
  284. assert isinstance(s.rendering_kw, dict)
  285. s = ContourSeries(x + y, (x, -2, 2), (y, -3, 3))
  286. assert isinstance(s.rendering_kw, dict)
  287. s = ParametricSurfaceSeries(1, x, y, (x, 0, 1), (y, 0, 1))
  288. assert isinstance(s.rendering_kw, dict)
  289. def test_data_shape():
  290. # Verify that the series produces the correct data shape when the input
  291. # expression is a number.
  292. if not np:
  293. skip("numpy not installed.")
  294. u, x, y, z = symbols("u, x:z")
  295. # scalar expression: it should return a numpy ones array
  296. s = LineOver1DRangeSeries(1, (x, -5, 5))
  297. xx, yy = s.get_data()
  298. assert len(xx) == len(yy)
  299. assert np.all(yy == 1)
  300. s = LineOver1DRangeSeries(1, (x, -5, 5), adaptive=False, n=10)
  301. xx, yy = s.get_data()
  302. assert len(xx) == len(yy) == 10
  303. assert np.all(yy == 1)
  304. s = Parametric2DLineSeries(sin(x), 1, (x, 0, pi))
  305. xx, yy, param = s.get_data()
  306. assert (len(xx) == len(yy)) and (len(xx) == len(param))
  307. assert np.all(yy == 1)
  308. s = Parametric2DLineSeries(1, sin(x), (x, 0, pi))
  309. xx, yy, param = s.get_data()
  310. assert (len(xx) == len(yy)) and (len(xx) == len(param))
  311. assert np.all(xx == 1)
  312. s = Parametric2DLineSeries(sin(x), 1, (x, 0, pi), adaptive=False)
  313. xx, yy, param = s.get_data()
  314. assert (len(xx) == len(yy)) and (len(xx) == len(param))
  315. assert np.all(yy == 1)
  316. s = Parametric2DLineSeries(1, sin(x), (x, 0, pi), adaptive=False)
  317. xx, yy, param = s.get_data()
  318. assert (len(xx) == len(yy)) and (len(xx) == len(param))
  319. assert np.all(xx == 1)
  320. s = Parametric3DLineSeries(cos(x), sin(x), 1, (x, 0, 2 * pi))
  321. xx, yy, zz, param = s.get_data()
  322. assert (len(xx) == len(yy)) and (len(xx) == len(zz)) and (len(xx) == len(param))
  323. assert np.all(zz == 1)
  324. s = Parametric3DLineSeries(cos(x), 1, x, (x, 0, 2 * pi))
  325. xx, yy, zz, param = s.get_data()
  326. assert (len(xx) == len(yy)) and (len(xx) == len(zz)) and (len(xx) == len(param))
  327. assert np.all(yy == 1)
  328. s = Parametric3DLineSeries(1, sin(x), x, (x, 0, 2 * pi))
  329. xx, yy, zz, param = s.get_data()
  330. assert (len(xx) == len(yy)) and (len(xx) == len(zz)) and (len(xx) == len(param))
  331. assert np.all(xx == 1)
  332. s = SurfaceOver2DRangeSeries(1, (x, -2, 2), (y, -3, 3))
  333. xx, yy, zz = s.get_data()
  334. assert (xx.shape == yy.shape) and (xx.shape == zz.shape)
  335. assert np.all(zz == 1)
  336. s = ParametricSurfaceSeries(1, x, y, (x, 0, 1), (y, 0, 1))
  337. xx, yy, zz, uu, vv = s.get_data()
  338. assert xx.shape == yy.shape == zz.shape == uu.shape == vv.shape
  339. assert np.all(xx == 1)
  340. s = ParametricSurfaceSeries(1, 1, y, (x, 0, 1), (y, 0, 1))
  341. xx, yy, zz, uu, vv = s.get_data()
  342. assert xx.shape == yy.shape == zz.shape == uu.shape == vv.shape
  343. assert np.all(yy == 1)
  344. s = ParametricSurfaceSeries(x, 1, 1, (x, 0, 1), (y, 0, 1))
  345. xx, yy, zz, uu, vv = s.get_data()
  346. assert xx.shape == yy.shape == zz.shape == uu.shape == vv.shape
  347. assert np.all(zz == 1)
  348. def test_only_integers():
  349. if not np:
  350. skip("numpy not installed.")
  351. x, y, u, v = symbols("x, y, u, v")
  352. s = LineOver1DRangeSeries(sin(x), (x, -5.5, 4.5), "",
  353. adaptive=False, only_integers=True)
  354. xx, _ = s.get_data()
  355. assert len(xx) == 10
  356. assert xx[0] == -5 and xx[-1] == 4
  357. s = Parametric2DLineSeries(cos(x), sin(x), (x, 0, 2 * pi), "",
  358. adaptive=False, only_integers=True)
  359. _, _, p = s.get_data()
  360. assert len(p) == 7
  361. assert p[0] == 0 and p[-1] == 6
  362. s = Parametric3DLineSeries(cos(x), sin(x), x, (x, 0, 2 * pi), "",
  363. adaptive=False, only_integers=True)
  364. _, _, _, p = s.get_data()
  365. assert len(p) == 7
  366. assert p[0] == 0 and p[-1] == 6
  367. s = SurfaceOver2DRangeSeries(cos(x**2 + y**2), (x, -5.5, 5.5),
  368. (y, -3.5, 3.5), "",
  369. adaptive=False, only_integers=True)
  370. xx, yy, _ = s.get_data()
  371. assert xx.shape == yy.shape == (7, 11)
  372. assert np.allclose(xx[:, 0] - (-5) * np.ones(7), 0)
  373. assert np.allclose(xx[0, :] - np.linspace(-5, 5, 11), 0)
  374. assert np.allclose(yy[:, 0] - np.linspace(-3, 3, 7), 0)
  375. assert np.allclose(yy[0, :] - (-3) * np.ones(11), 0)
  376. r = 2 + sin(7 * u + 5 * v)
  377. expr = (
  378. r * cos(u) * sin(v),
  379. r * sin(u) * sin(v),
  380. r * cos(v)
  381. )
  382. s = ParametricSurfaceSeries(*expr, (u, 0, 2 * pi), (v, 0, pi), "",
  383. adaptive=False, only_integers=True)
  384. xx, yy, zz, uu, vv = s.get_data()
  385. assert xx.shape == yy.shape == zz.shape == uu.shape == vv.shape == (4, 7)
  386. # only_integers also works with scalar expressions
  387. s = LineOver1DRangeSeries(1, (x, -5.5, 4.5), "",
  388. adaptive=False, only_integers=True)
  389. xx, _ = s.get_data()
  390. assert len(xx) == 10
  391. assert xx[0] == -5 and xx[-1] == 4
  392. s = Parametric2DLineSeries(cos(x), 1, (x, 0, 2 * pi), "",
  393. adaptive=False, only_integers=True)
  394. _, _, p = s.get_data()
  395. assert len(p) == 7
  396. assert p[0] == 0 and p[-1] == 6
  397. s = SurfaceOver2DRangeSeries(1, (x, -5.5, 5.5), (y, -3.5, 3.5), "",
  398. adaptive=False, only_integers=True)
  399. xx, yy, _ = s.get_data()
  400. assert xx.shape == yy.shape == (7, 11)
  401. assert np.allclose(xx[:, 0] - (-5) * np.ones(7), 0)
  402. assert np.allclose(xx[0, :] - np.linspace(-5, 5, 11), 0)
  403. assert np.allclose(yy[:, 0] - np.linspace(-3, 3, 7), 0)
  404. assert np.allclose(yy[0, :] - (-3) * np.ones(11), 0)
  405. r = 2 + sin(7 * u + 5 * v)
  406. expr = (
  407. r * cos(u) * sin(v),
  408. 1,
  409. r * cos(v)
  410. )
  411. s = ParametricSurfaceSeries(*expr, (u, 0, 2 * pi), (v, 0, pi), "",
  412. adaptive=False, only_integers=True)
  413. xx, yy, zz, uu, vv = s.get_data()
  414. assert xx.shape == yy.shape == zz.shape == uu.shape == vv.shape == (4, 7)
  415. def test_is_point_is_filled():
  416. # verify that `is_point` and `is_filled` are attributes and that they
  417. # they receive the correct values
  418. if not np:
  419. skip("numpy not installed.")
  420. x, u = symbols("x, u")
  421. s = LineOver1DRangeSeries(cos(x), (x, -5, 5), "",
  422. is_point=False, is_filled=True)
  423. assert (not s.is_point) and s.is_filled
  424. s = LineOver1DRangeSeries(cos(x), (x, -5, 5), "",
  425. is_point=True, is_filled=False)
  426. assert s.is_point and (not s.is_filled)
  427. s = List2DSeries([0, 1, 2], [3, 4, 5],
  428. is_point=False, is_filled=True)
  429. assert (not s.is_point) and s.is_filled
  430. s = List2DSeries([0, 1, 2], [3, 4, 5],
  431. is_point=True, is_filled=False)
  432. assert s.is_point and (not s.is_filled)
  433. s = Parametric2DLineSeries(cos(x), sin(x), (x, -5, 5),
  434. is_point=False, is_filled=True)
  435. assert (not s.is_point) and s.is_filled
  436. s = Parametric2DLineSeries(cos(x), sin(x), (x, -5, 5),
  437. is_point=True, is_filled=False)
  438. assert s.is_point and (not s.is_filled)
  439. s = Parametric3DLineSeries(cos(x), sin(x), x, (x, -5, 5),
  440. is_point=False, is_filled=True)
  441. assert (not s.is_point) and s.is_filled
  442. s = Parametric3DLineSeries(cos(x), sin(x), x, (x, -5, 5),
  443. is_point=True, is_filled=False)
  444. assert s.is_point and (not s.is_filled)
  445. def test_is_filled_2d():
  446. # verify that the is_filled attribute is exposed by the following series
  447. x, y = symbols("x, y")
  448. expr = cos(x**2 + y**2)
  449. ranges = (x, -2, 2), (y, -2, 2)
  450. s = ContourSeries(expr, *ranges)
  451. assert s.is_filled
  452. s = ContourSeries(expr, *ranges, is_filled=True)
  453. assert s.is_filled
  454. s = ContourSeries(expr, *ranges, is_filled=False)
  455. assert not s.is_filled
  456. def test_steps():
  457. if not np:
  458. skip("numpy not installed.")
  459. x, u = symbols("x, u")
  460. def do_test(s1, s2):
  461. if (not s1.is_parametric) and s1.is_2Dline:
  462. xx1, _ = s1.get_data()
  463. xx2, _ = s2.get_data()
  464. elif s1.is_parametric and s1.is_2Dline:
  465. xx1, _, _ = s1.get_data()
  466. xx2, _, _ = s2.get_data()
  467. elif (not s1.is_parametric) and s1.is_3Dline:
  468. xx1, _, _ = s1.get_data()
  469. xx2, _, _ = s2.get_data()
  470. else:
  471. xx1, _, _, _ = s1.get_data()
  472. xx2, _, _, _ = s2.get_data()
  473. assert len(xx1) != len(xx2)
  474. s1 = LineOver1DRangeSeries(cos(x), (x, -5, 5), "",
  475. adaptive=False, n=40, steps=False)
  476. s2 = LineOver1DRangeSeries(cos(x), (x, -5, 5), "",
  477. adaptive=False, n=40, steps=True)
  478. do_test(s1, s2)
  479. s1 = List2DSeries([0, 1, 2], [3, 4, 5], steps=False)
  480. s2 = List2DSeries([0, 1, 2], [3, 4, 5], steps=True)
  481. do_test(s1, s2)
  482. s1 = Parametric2DLineSeries(cos(x), sin(x), (x, -5, 5),
  483. adaptive=False, n=40, steps=False)
  484. s2 = Parametric2DLineSeries(cos(x), sin(x), (x, -5, 5),
  485. adaptive=False, n=40, steps=True)
  486. do_test(s1, s2)
  487. s1 = Parametric3DLineSeries(cos(x), sin(x), x, (x, -5, 5),
  488. adaptive=False, n=40, steps=False)
  489. s2 = Parametric3DLineSeries(cos(x), sin(x), x, (x, -5, 5),
  490. adaptive=False, n=40, steps=True)
  491. do_test(s1, s2)
  492. def test_interactive_data():
  493. # verify that InteractiveSeries produces the same numerical data as their
  494. # corresponding non-interactive series.
  495. if not np:
  496. skip("numpy not installed.")
  497. u, x, y, z = symbols("u, x:z")
  498. def do_test(data1, data2):
  499. assert len(data1) == len(data2)
  500. for d1, d2 in zip(data1, data2):
  501. assert np.allclose(d1, d2)
  502. s1 = LineOver1DRangeSeries(u * cos(x), (x, -5, 5), params={u: 1}, n=50)
  503. s2 = LineOver1DRangeSeries(cos(x), (x, -5, 5), adaptive=False, n=50)
  504. do_test(s1.get_data(), s2.get_data())
  505. s1 = Parametric2DLineSeries(
  506. u * cos(x), u * sin(x), (x, -5, 5), params={u: 1}, n=50)
  507. s2 = Parametric2DLineSeries(cos(x), sin(x), (x, -5, 5),
  508. adaptive=False, n=50)
  509. do_test(s1.get_data(), s2.get_data())
  510. s1 = Parametric3DLineSeries(
  511. u * cos(x), u * sin(x), u * x, (x, -5, 5),
  512. params={u: 1}, n=50)
  513. s2 = Parametric3DLineSeries(cos(x), sin(x), x, (x, -5, 5),
  514. adaptive=False, n=50)
  515. do_test(s1.get_data(), s2.get_data())
  516. s1 = SurfaceOver2DRangeSeries(
  517. u * cos(x ** 2 + y ** 2), (x, -3, 3), (y, -3, 3),
  518. params={u: 1}, n1=50, n2=50,)
  519. s2 = SurfaceOver2DRangeSeries(
  520. cos(x ** 2 + y ** 2), (x, -3, 3), (y, -3, 3),
  521. adaptive=False, n1=50, n2=50)
  522. do_test(s1.get_data(), s2.get_data())
  523. s1 = ParametricSurfaceSeries(
  524. u * cos(x + y), sin(x + y), x - y, (x, -3, 3), (y, -3, 3),
  525. params={u: 1}, n1=50, n2=50,)
  526. s2 = ParametricSurfaceSeries(
  527. cos(x + y), sin(x + y), x - y, (x, -3, 3), (y, -3, 3),
  528. adaptive=False, n1=50, n2=50,)
  529. do_test(s1.get_data(), s2.get_data())
  530. # real part of a complex function evaluated over a real line with numpy
  531. expr = re((z ** 2 + 1) / (z ** 2 - 1))
  532. s1 = LineOver1DRangeSeries(u * expr, (z, -3, 3), adaptive=False, n=50,
  533. modules=None, params={u: 1})
  534. s2 = LineOver1DRangeSeries(expr, (z, -3, 3), adaptive=False, n=50,
  535. modules=None)
  536. do_test(s1.get_data(), s2.get_data())
  537. # real part of a complex function evaluated over a real line with mpmath
  538. expr = re((z ** 2 + 1) / (z ** 2 - 1))
  539. s1 = LineOver1DRangeSeries(u * expr, (z, -3, 3), n=50, modules="mpmath",
  540. params={u: 1})
  541. s2 = LineOver1DRangeSeries(expr, (z, -3, 3),
  542. adaptive=False, n=50, modules="mpmath")
  543. do_test(s1.get_data(), s2.get_data())
  544. def test_list2dseries_interactive():
  545. if not np:
  546. skip("numpy not installed.")
  547. x, y, u = symbols("x, y, u")
  548. s = List2DSeries([1, 2, 3], [1, 2, 3])
  549. assert not s.is_interactive
  550. # symbolic expressions as coordinates, but no ``params``
  551. raises(ValueError, lambda: List2DSeries([cos(x)], [sin(x)]))
  552. # too few parameters
  553. raises(ValueError,
  554. lambda: List2DSeries([cos(x), y], [sin(x), 2], params={u: 1}))
  555. s = List2DSeries([cos(x)], [sin(x)], params={x: 1})
  556. assert s.is_interactive
  557. s = List2DSeries([x, 2, 3, 4], [4, 3, 2, x], params={x: 3})
  558. xx, yy = s.get_data()
  559. assert np.allclose(xx, [3, 2, 3, 4])
  560. assert np.allclose(yy, [4, 3, 2, 3])
  561. assert not s.is_parametric
  562. # numeric lists + params is present -> interactive series and
  563. # lists are converted to Tuple.
  564. s = List2DSeries([1, 2, 3], [1, 2, 3], params={x: 1})
  565. assert s.is_interactive
  566. assert isinstance(s.list_x, Tuple)
  567. assert isinstance(s.list_y, Tuple)
  568. def test_mpmath():
  569. # test that the argument of complex functions evaluated with mpmath
  570. # might be different than the one computed with Numpy (different
  571. # behaviour at branch cuts)
  572. if not np:
  573. skip("numpy not installed.")
  574. z, u = symbols("z, u")
  575. s1 = LineOver1DRangeSeries(im(sqrt(-z)), (z, 1e-03, 5),
  576. adaptive=True, modules=None, force_real_eval=True)
  577. s2 = LineOver1DRangeSeries(im(sqrt(-z)), (z, 1e-03, 5),
  578. adaptive=True, modules="mpmath", force_real_eval=True)
  579. xx1, yy1 = s1.get_data()
  580. xx2, yy2 = s2.get_data()
  581. assert np.all(yy1 < 0)
  582. assert np.all(yy2 > 0)
  583. s1 = LineOver1DRangeSeries(im(sqrt(-z)), (z, -5, 5),
  584. adaptive=False, n=20, modules=None, force_real_eval=True)
  585. s2 = LineOver1DRangeSeries(im(sqrt(-z)), (z, -5, 5),
  586. adaptive=False, n=20, modules="mpmath", force_real_eval=True)
  587. xx1, yy1 = s1.get_data()
  588. xx2, yy2 = s2.get_data()
  589. assert np.allclose(xx1, xx2)
  590. assert not np.allclose(yy1, yy2)
  591. def test_str():
  592. u, x, y, z = symbols("u, x:z")
  593. s = LineOver1DRangeSeries(cos(x), (x, -4, 3))
  594. assert str(s) == "cartesian line: cos(x) for x over (-4.0, 3.0)"
  595. d = {"return": "real"}
  596. s = LineOver1DRangeSeries(cos(x), (x, -4, 3), **d)
  597. assert str(s) == "cartesian line: re(cos(x)) for x over (-4.0, 3.0)"
  598. d = {"return": "imag"}
  599. s = LineOver1DRangeSeries(cos(x), (x, -4, 3), **d)
  600. assert str(s) == "cartesian line: im(cos(x)) for x over (-4.0, 3.0)"
  601. d = {"return": "abs"}
  602. s = LineOver1DRangeSeries(cos(x), (x, -4, 3), **d)
  603. assert str(s) == "cartesian line: abs(cos(x)) for x over (-4.0, 3.0)"
  604. d = {"return": "arg"}
  605. s = LineOver1DRangeSeries(cos(x), (x, -4, 3), **d)
  606. assert str(s) == "cartesian line: arg(cos(x)) for x over (-4.0, 3.0)"
  607. s = LineOver1DRangeSeries(cos(u * x), (x, -4, 3), params={u: 1})
  608. assert str(s) == "interactive cartesian line: cos(u*x) for x over (-4.0, 3.0) and parameters (u,)"
  609. s = LineOver1DRangeSeries(cos(u * x), (x, -u, 3*y), params={u: 1, y: 1})
  610. assert str(s) == "interactive cartesian line: cos(u*x) for x over (-u, 3*y) and parameters (u, y)"
  611. s = Parametric2DLineSeries(cos(x), sin(x), (x, -4, 3))
  612. assert str(s) == "parametric cartesian line: (cos(x), sin(x)) for x over (-4.0, 3.0)"
  613. s = Parametric2DLineSeries(cos(u * x), sin(x), (x, -4, 3), params={u: 1})
  614. assert str(s) == "interactive parametric cartesian line: (cos(u*x), sin(x)) for x over (-4.0, 3.0) and parameters (u,)"
  615. s = Parametric2DLineSeries(cos(u * x), sin(x), (x, -u, 3*y), params={u: 1, y:1})
  616. assert str(s) == "interactive parametric cartesian line: (cos(u*x), sin(x)) for x over (-u, 3*y) and parameters (u, y)"
  617. s = Parametric3DLineSeries(cos(x), sin(x), x, (x, -4, 3))
  618. assert str(s) == "3D parametric cartesian line: (cos(x), sin(x), x) for x over (-4.0, 3.0)"
  619. s = Parametric3DLineSeries(cos(u*x), sin(x), x, (x, -4, 3), params={u: 1})
  620. assert str(s) == "interactive 3D parametric cartesian line: (cos(u*x), sin(x), x) for x over (-4.0, 3.0) and parameters (u,)"
  621. s = Parametric3DLineSeries(cos(u*x), sin(x), x, (x, -u, 3*y), params={u: 1, y: 1})
  622. assert str(s) == "interactive 3D parametric cartesian line: (cos(u*x), sin(x), x) for x over (-u, 3*y) and parameters (u, y)"
  623. s = SurfaceOver2DRangeSeries(cos(x * y), (x, -4, 3), (y, -2, 5))
  624. assert str(s) == "cartesian surface: cos(x*y) for x over (-4.0, 3.0) and y over (-2.0, 5.0)"
  625. s = SurfaceOver2DRangeSeries(cos(u * x * y), (x, -4, 3), (y, -2, 5), params={u: 1})
  626. assert str(s) == "interactive cartesian surface: cos(u*x*y) for x over (-4.0, 3.0) and y over (-2.0, 5.0) and parameters (u,)"
  627. s = SurfaceOver2DRangeSeries(cos(u * x * y), (x, -4*u, 3), (y, -2, 5*u), params={u: 1})
  628. assert str(s) == "interactive cartesian surface: cos(u*x*y) for x over (-4*u, 3.0) and y over (-2.0, 5*u) and parameters (u,)"
  629. s = ContourSeries(cos(x * y), (x, -4, 3), (y, -2, 5))
  630. assert str(s) == "contour: cos(x*y) for x over (-4.0, 3.0) and y over (-2.0, 5.0)"
  631. s = ContourSeries(cos(u * x * y), (x, -4, 3), (y, -2, 5), params={u: 1})
  632. assert str(s) == "interactive contour: cos(u*x*y) for x over (-4.0, 3.0) and y over (-2.0, 5.0) and parameters (u,)"
  633. s = ParametricSurfaceSeries(cos(x * y), sin(x * y), x * y,
  634. (x, -4, 3), (y, -2, 5))
  635. assert str(s) == "parametric cartesian surface: (cos(x*y), sin(x*y), x*y) for x over (-4.0, 3.0) and y over (-2.0, 5.0)"
  636. s = ParametricSurfaceSeries(cos(u * x * y), sin(x * y), x * y,
  637. (x, -4, 3), (y, -2, 5), params={u: 1})
  638. assert str(s) == "interactive parametric cartesian surface: (cos(u*x*y), sin(x*y), x*y) for x over (-4.0, 3.0) and y over (-2.0, 5.0) and parameters (u,)"
  639. s = ImplicitSeries(x < y, (x, -5, 4), (y, -3, 2))
  640. assert str(s) == "Implicit expression: x < y for x over (-5.0, 4.0) and y over (-3.0, 2.0)"
  641. def test_use_cm():
  642. # verify that the `use_cm` attribute is implemented.
  643. if not np:
  644. skip("numpy not installed.")
  645. u, x, y, z = symbols("u, x:z")
  646. s = List2DSeries([1, 2, 3, 4], [5, 6, 7, 8], use_cm=True)
  647. assert s.use_cm
  648. s = List2DSeries([1, 2, 3, 4], [5, 6, 7, 8], use_cm=False)
  649. assert not s.use_cm
  650. s = Parametric2DLineSeries(cos(x), sin(x), (x, -4, 3), use_cm=True)
  651. assert s.use_cm
  652. s = Parametric2DLineSeries(cos(x), sin(x), (x, -4, 3), use_cm=False)
  653. assert not s.use_cm
  654. s = Parametric3DLineSeries(cos(x), sin(x), x, (x, -4, 3),
  655. use_cm=True)
  656. assert s.use_cm
  657. s = Parametric3DLineSeries(cos(x), sin(x), x, (x, -4, 3),
  658. use_cm=False)
  659. assert not s.use_cm
  660. s = SurfaceOver2DRangeSeries(cos(x * y), (x, -4, 3), (y, -2, 5),
  661. use_cm=True)
  662. assert s.use_cm
  663. s = SurfaceOver2DRangeSeries(cos(x * y), (x, -4, 3), (y, -2, 5),
  664. use_cm=False)
  665. assert not s.use_cm
  666. s = ParametricSurfaceSeries(cos(x * y), sin(x * y), x * y,
  667. (x, -4, 3), (y, -2, 5), use_cm=True)
  668. assert s.use_cm
  669. s = ParametricSurfaceSeries(cos(x * y), sin(x * y), x * y,
  670. (x, -4, 3), (y, -2, 5), use_cm=False)
  671. assert not s.use_cm
  672. def test_surface_use_cm():
  673. # verify that SurfaceOver2DRangeSeries and ParametricSurfaceSeries get
  674. # the same value for use_cm
  675. x, y, u, v = symbols("x, y, u, v")
  676. # they read the same value from default settings
  677. s1 = SurfaceOver2DRangeSeries(cos(x**2 + y**2), (x, -2, 2), (y, -2, 2))
  678. s2 = ParametricSurfaceSeries(u * cos(v), u * sin(v), u,
  679. (u, 0, 1), (v, 0 , 2*pi))
  680. assert s1.use_cm == s2.use_cm
  681. # they get the same value
  682. s1 = SurfaceOver2DRangeSeries(cos(x**2 + y**2), (x, -2, 2), (y, -2, 2),
  683. use_cm=False)
  684. s2 = ParametricSurfaceSeries(u * cos(v), u * sin(v), u,
  685. (u, 0, 1), (v, 0 , 2*pi), use_cm=False)
  686. assert s1.use_cm == s2.use_cm
  687. # they get the same value
  688. s1 = SurfaceOver2DRangeSeries(cos(x**2 + y**2), (x, -2, 2), (y, -2, 2),
  689. use_cm=True)
  690. s2 = ParametricSurfaceSeries(u * cos(v), u * sin(v), u,
  691. (u, 0, 1), (v, 0 , 2*pi), use_cm=True)
  692. assert s1.use_cm == s2.use_cm
  693. def test_sums():
  694. # test that data series are able to deal with sums
  695. if not np:
  696. skip("numpy not installed.")
  697. x, y, u = symbols("x, y, u")
  698. def do_test(data1, data2):
  699. assert len(data1) == len(data2)
  700. for d1, d2 in zip(data1, data2):
  701. assert np.allclose(d1, d2)
  702. s = LineOver1DRangeSeries(Sum(1 / x ** y, (x, 1, 1000)), (y, 2, 10),
  703. adaptive=False, only_integers=True)
  704. xx, yy = s.get_data()
  705. s1 = LineOver1DRangeSeries(Sum(1 / x, (x, 1, y)), (y, 2, 10),
  706. adaptive=False, only_integers=True)
  707. xx1, yy1 = s1.get_data()
  708. s2 = LineOver1DRangeSeries(Sum(u / x, (x, 1, y)), (y, 2, 10),
  709. params={u: 1}, only_integers=True)
  710. xx2, yy2 = s2.get_data()
  711. xx1 = xx1.astype(float)
  712. xx2 = xx2.astype(float)
  713. do_test([xx1, yy1], [xx2, yy2])
  714. s = LineOver1DRangeSeries(Sum(1 / x, (x, 1, y)), (y, 2, 10),
  715. adaptive=True)
  716. with warns(
  717. UserWarning,
  718. match="The evaluation with NumPy/SciPy failed",
  719. test_stacklevel=False,
  720. ):
  721. raises(TypeError, lambda: s.get_data())
  722. def test_apply_transforms():
  723. # verify that transformation functions get applied to the output
  724. # of data series
  725. if not np:
  726. skip("numpy not installed.")
  727. x, y, z, u, v = symbols("x:z, u, v")
  728. s1 = LineOver1DRangeSeries(cos(x), (x, -2*pi, 2*pi), adaptive=False, n=10)
  729. s2 = LineOver1DRangeSeries(cos(x), (x, -2*pi, 2*pi), adaptive=False, n=10,
  730. tx=np.rad2deg)
  731. s3 = LineOver1DRangeSeries(cos(x), (x, -2*pi, 2*pi), adaptive=False, n=10,
  732. ty=np.rad2deg)
  733. s4 = LineOver1DRangeSeries(cos(x), (x, -2*pi, 2*pi), adaptive=False, n=10,
  734. tx=np.rad2deg, ty=np.rad2deg)
  735. x1, y1 = s1.get_data()
  736. x2, y2 = s2.get_data()
  737. x3, y3 = s3.get_data()
  738. x4, y4 = s4.get_data()
  739. assert np.isclose(x1[0], -2*np.pi) and np.isclose(x1[-1], 2*np.pi)
  740. assert (y1.min() < -0.9) and (y1.max() > 0.9)
  741. assert np.isclose(x2[0], -360) and np.isclose(x2[-1], 360)
  742. assert (y2.min() < -0.9) and (y2.max() > 0.9)
  743. assert np.isclose(x3[0], -2*np.pi) and np.isclose(x3[-1], 2*np.pi)
  744. assert (y3.min() < -52) and (y3.max() > 52)
  745. assert np.isclose(x4[0], -360) and np.isclose(x4[-1], 360)
  746. assert (y4.min() < -52) and (y4.max() > 52)
  747. xx = np.linspace(-2*np.pi, 2*np.pi, 10)
  748. yy = np.cos(xx)
  749. s1 = List2DSeries(xx, yy)
  750. s2 = List2DSeries(xx, yy, tx=np.rad2deg, ty=np.rad2deg)
  751. x1, y1 = s1.get_data()
  752. x2, y2 = s2.get_data()
  753. assert np.isclose(x1[0], -2*np.pi) and np.isclose(x1[-1], 2*np.pi)
  754. assert (y1.min() < -0.9) and (y1.max() > 0.9)
  755. assert np.isclose(x2[0], -360) and np.isclose(x2[-1], 360)
  756. assert (y2.min() < -52) and (y2.max() > 52)
  757. s1 = Parametric2DLineSeries(
  758. sin(x), cos(x), (x, -pi, pi), adaptive=False, n=10)
  759. s2 = Parametric2DLineSeries(
  760. sin(x), cos(x), (x, -pi, pi), adaptive=False, n=10,
  761. tx=np.rad2deg, ty=np.rad2deg, tp=np.rad2deg)
  762. x1, y1, a1 = s1.get_data()
  763. x2, y2, a2 = s2.get_data()
  764. assert np.allclose(x1, np.deg2rad(x2))
  765. assert np.allclose(y1, np.deg2rad(y2))
  766. assert np.allclose(a1, np.deg2rad(a2))
  767. s1 = Parametric3DLineSeries(
  768. sin(x), cos(x), x, (x, -pi, pi), adaptive=False, n=10)
  769. s2 = Parametric3DLineSeries(
  770. sin(x), cos(x), x, (x, -pi, pi), adaptive=False, n=10, tp=np.rad2deg)
  771. x1, y1, z1, a1 = s1.get_data()
  772. x2, y2, z2, a2 = s2.get_data()
  773. assert np.allclose(x1, x2)
  774. assert np.allclose(y1, y2)
  775. assert np.allclose(z1, z2)
  776. assert np.allclose(a1, np.deg2rad(a2))
  777. s1 = SurfaceOver2DRangeSeries(
  778. cos(x**2 + y**2), (x, -2*pi, 2*pi), (y, -2*pi, 2*pi),
  779. adaptive=False, n1=10, n2=10)
  780. s2 = SurfaceOver2DRangeSeries(
  781. cos(x**2 + y**2), (x, -2*pi, 2*pi), (y, -2*pi, 2*pi),
  782. adaptive=False, n1=10, n2=10,
  783. tx=np.rad2deg, ty=lambda x: 2*x, tz=lambda x: 3*x)
  784. x1, y1, z1 = s1.get_data()
  785. x2, y2, z2 = s2.get_data()
  786. assert np.allclose(x1, np.deg2rad(x2))
  787. assert np.allclose(y1, y2 / 2)
  788. assert np.allclose(z1, z2 / 3)
  789. s1 = ParametricSurfaceSeries(
  790. u + v, u - v, u * v, (u, 0, 2*pi), (v, 0, pi),
  791. adaptive=False, n1=10, n2=10)
  792. s2 = ParametricSurfaceSeries(
  793. u + v, u - v, u * v, (u, 0, 2*pi), (v, 0, pi),
  794. adaptive=False, n1=10, n2=10,
  795. tx=np.rad2deg, ty=lambda x: 2*x, tz=lambda x: 3*x)
  796. x1, y1, z1, u1, v1 = s1.get_data()
  797. x2, y2, z2, u2, v2 = s2.get_data()
  798. assert np.allclose(x1, np.deg2rad(x2))
  799. assert np.allclose(y1, y2 / 2)
  800. assert np.allclose(z1, z2 / 3)
  801. assert np.allclose(u1, u2)
  802. assert np.allclose(v1, v2)
  803. def test_series_labels():
  804. # verify that series return the correct label, depending on the plot
  805. # type and input arguments. If the user set custom label on a data series,
  806. # it should returned un-modified.
  807. if not np:
  808. skip("numpy not installed.")
  809. x, y, z, u, v = symbols("x, y, z, u, v")
  810. wrapper = "$%s$"
  811. expr = cos(x)
  812. s1 = LineOver1DRangeSeries(expr, (x, -2, 2), None)
  813. s2 = LineOver1DRangeSeries(expr, (x, -2, 2), "test")
  814. assert s1.get_label(False) == str(expr)
  815. assert s1.get_label(True) == wrapper % latex(expr)
  816. assert s2.get_label(False) == "test"
  817. assert s2.get_label(True) == "test"
  818. s1 = List2DSeries([0, 1, 2, 3], [0, 1, 2, 3], "test")
  819. assert s1.get_label(False) == "test"
  820. assert s1.get_label(True) == "test"
  821. expr = (cos(x), sin(x))
  822. s1 = Parametric2DLineSeries(*expr, (x, -2, 2), None, use_cm=True)
  823. s2 = Parametric2DLineSeries(*expr, (x, -2, 2), "test", use_cm=True)
  824. s3 = Parametric2DLineSeries(*expr, (x, -2, 2), None, use_cm=False)
  825. s4 = Parametric2DLineSeries(*expr, (x, -2, 2), "test", use_cm=False)
  826. assert s1.get_label(False) == "x"
  827. assert s1.get_label(True) == wrapper % "x"
  828. assert s2.get_label(False) == "test"
  829. assert s2.get_label(True) == "test"
  830. assert s3.get_label(False) == str(expr)
  831. assert s3.get_label(True) == wrapper % latex(expr)
  832. assert s4.get_label(False) == "test"
  833. assert s4.get_label(True) == "test"
  834. expr = (cos(x), sin(x), x)
  835. s1 = Parametric3DLineSeries(*expr, (x, -2, 2), None, use_cm=True)
  836. s2 = Parametric3DLineSeries(*expr, (x, -2, 2), "test", use_cm=True)
  837. s3 = Parametric3DLineSeries(*expr, (x, -2, 2), None, use_cm=False)
  838. s4 = Parametric3DLineSeries(*expr, (x, -2, 2), "test", use_cm=False)
  839. assert s1.get_label(False) == "x"
  840. assert s1.get_label(True) == wrapper % "x"
  841. assert s2.get_label(False) == "test"
  842. assert s2.get_label(True) == "test"
  843. assert s3.get_label(False) == str(expr)
  844. assert s3.get_label(True) == wrapper % latex(expr)
  845. assert s4.get_label(False) == "test"
  846. assert s4.get_label(True) == "test"
  847. expr = cos(x**2 + y**2)
  848. s1 = SurfaceOver2DRangeSeries(expr, (x, -2, 2), (y, -2, 2), None)
  849. s2 = SurfaceOver2DRangeSeries(expr, (x, -2, 2), (y, -2, 2), "test")
  850. assert s1.get_label(False) == str(expr)
  851. assert s1.get_label(True) == wrapper % latex(expr)
  852. assert s2.get_label(False) == "test"
  853. assert s2.get_label(True) == "test"
  854. expr = (cos(x - y), sin(x + y), x - y)
  855. s1 = ParametricSurfaceSeries(*expr, (x, -2, 2), (y, -2, 2), None)
  856. s2 = ParametricSurfaceSeries(*expr, (x, -2, 2), (y, -2, 2), "test")
  857. assert s1.get_label(False) == str(expr)
  858. assert s1.get_label(True) == wrapper % latex(expr)
  859. assert s2.get_label(False) == "test"
  860. assert s2.get_label(True) == "test"
  861. expr = Eq(cos(x - y), 0)
  862. s1 = ImplicitSeries(expr, (x, -10, 10), (y, -10, 10), None)
  863. s2 = ImplicitSeries(expr, (x, -10, 10), (y, -10, 10), "test")
  864. assert s1.get_label(False) == str(expr)
  865. assert s1.get_label(True) == wrapper % latex(expr)
  866. assert s2.get_label(False) == "test"
  867. assert s2.get_label(True) == "test"
  868. def test_is_polar_2d_parametric():
  869. # verify that Parametric2DLineSeries isable to apply polar discretization,
  870. # which is used when polar_plot is executed with polar_axis=True
  871. if not np:
  872. skip("numpy not installed.")
  873. t, u = symbols("t u")
  874. # NOTE: a sufficiently big n must be provided, or else tests
  875. # are going to fail
  876. # No colormap
  877. f = sin(4 * t)
  878. s1 = Parametric2DLineSeries(f * cos(t), f * sin(t), (t, 0, 2*pi),
  879. adaptive=False, n=10, is_polar=False, use_cm=False)
  880. x1, y1, p1 = s1.get_data()
  881. s2 = Parametric2DLineSeries(f * cos(t), f * sin(t), (t, 0, 2*pi),
  882. adaptive=False, n=10, is_polar=True, use_cm=False)
  883. th, r, p2 = s2.get_data()
  884. assert (not np.allclose(x1, th)) and (not np.allclose(y1, r))
  885. assert np.allclose(p1, p2)
  886. # With colormap
  887. s3 = Parametric2DLineSeries(f * cos(t), f * sin(t), (t, 0, 2*pi),
  888. adaptive=False, n=10, is_polar=False, color_func=lambda t: 2*t)
  889. x3, y3, p3 = s3.get_data()
  890. s4 = Parametric2DLineSeries(f * cos(t), f * sin(t), (t, 0, 2*pi),
  891. adaptive=False, n=10, is_polar=True, color_func=lambda t: 2*t)
  892. th4, r4, p4 = s4.get_data()
  893. assert np.allclose(p3, p4) and (not np.allclose(p1, p3))
  894. assert np.allclose(x3, x1) and np.allclose(y3, y1)
  895. assert np.allclose(th4, th) and np.allclose(r4, r)
  896. def test_is_polar_3d():
  897. # verify that SurfaceOver2DRangeSeries is able to apply
  898. # polar discretization
  899. if not np:
  900. skip("numpy not installed.")
  901. x, y, t = symbols("x, y, t")
  902. expr = (x**2 - 1)**2
  903. s1 = SurfaceOver2DRangeSeries(expr, (x, 0, 1.5), (y, 0, 2 * pi),
  904. n=10, adaptive=False, is_polar=False)
  905. s2 = SurfaceOver2DRangeSeries(expr, (x, 0, 1.5), (y, 0, 2 * pi),
  906. n=10, adaptive=False, is_polar=True)
  907. x1, y1, z1 = s1.get_data()
  908. x2, y2, z2 = s2.get_data()
  909. x22, y22 = x1 * np.cos(y1), x1 * np.sin(y1)
  910. assert np.allclose(x2, x22)
  911. assert np.allclose(y2, y22)
  912. def test_color_func():
  913. # verify that eval_color_func produces the expected results in order to
  914. # maintain back compatibility with the old sympy.plotting module
  915. if not np:
  916. skip("numpy not installed.")
  917. x, y, z, u, v = symbols("x, y, z, u, v")
  918. # color func: returns x, y, color and s is parametric
  919. xx = np.linspace(-3, 3, 10)
  920. yy1 = np.cos(xx)
  921. s = List2DSeries(xx, yy1, color_func=lambda x, y: 2 * x, use_cm=True)
  922. xxs, yys, col = s.get_data()
  923. assert np.allclose(xx, xxs)
  924. assert np.allclose(yy1, yys)
  925. assert np.allclose(2 * xx, col)
  926. assert s.is_parametric
  927. s = List2DSeries(xx, yy1, color_func=lambda x, y: 2 * x, use_cm=False)
  928. assert len(s.get_data()) == 2
  929. assert not s.is_parametric
  930. s = Parametric2DLineSeries(cos(x), sin(x), (x, 0, 2*pi),
  931. adaptive=False, n=10, color_func=lambda t: t)
  932. xx, yy, col = s.get_data()
  933. assert (not np.allclose(xx, col)) and (not np.allclose(yy, col))
  934. s = Parametric2DLineSeries(cos(x), sin(x), (x, 0, 2*pi),
  935. adaptive=False, n=10, color_func=lambda x, y: x * y)
  936. xx, yy, col = s.get_data()
  937. assert np.allclose(col, xx * yy)
  938. s = Parametric2DLineSeries(cos(x), sin(x), (x, 0, 2*pi),
  939. adaptive=False, n=10, color_func=lambda x, y, t: x * y * t)
  940. xx, yy, col = s.get_data()
  941. assert np.allclose(col, xx * yy * np.linspace(0, 2*np.pi, 10))
  942. s = Parametric3DLineSeries(cos(x), sin(x), x, (x, 0, 2*pi),
  943. adaptive=False, n=10, color_func=lambda t: t)
  944. xx, yy, zz, col = s.get_data()
  945. assert (not np.allclose(xx, col)) and (not np.allclose(yy, col))
  946. s = Parametric3DLineSeries(cos(x), sin(x), x, (x, 0, 2*pi),
  947. adaptive=False, n=10, color_func=lambda x, y, z: x * y * z)
  948. xx, yy, zz, col = s.get_data()
  949. assert np.allclose(col, xx * yy * zz)
  950. s = Parametric3DLineSeries(cos(x), sin(x), x, (x, 0, 2*pi),
  951. adaptive=False, n=10, color_func=lambda x, y, z, t: x * y * z * t)
  952. xx, yy, zz, col = s.get_data()
  953. assert np.allclose(col, xx * yy * zz * np.linspace(0, 2*np.pi, 10))
  954. s = SurfaceOver2DRangeSeries(cos(x**2 + y**2), (x, -2, 2), (y, -2, 2),
  955. adaptive=False, n1=10, n2=10, color_func=lambda x: x)
  956. xx, yy, zz = s.get_data()
  957. col = s.eval_color_func(xx, yy, zz)
  958. assert np.allclose(xx, col)
  959. s = SurfaceOver2DRangeSeries(cos(x**2 + y**2), (x, -2, 2), (y, -2, 2),
  960. adaptive=False, n1=10, n2=10, color_func=lambda x, y: x * y)
  961. xx, yy, zz = s.get_data()
  962. col = s.eval_color_func(xx, yy, zz)
  963. assert np.allclose(xx * yy, col)
  964. s = SurfaceOver2DRangeSeries(cos(x**2 + y**2), (x, -2, 2), (y, -2, 2),
  965. adaptive=False, n1=10, n2=10, color_func=lambda x, y, z: x * y * z)
  966. xx, yy, zz = s.get_data()
  967. col = s.eval_color_func(xx, yy, zz)
  968. assert np.allclose(xx * yy * zz, col)
  969. s = ParametricSurfaceSeries(1, x, y, (x, 0, 1), (y, 0, 1), adaptive=False,
  970. n1=10, n2=10, color_func=lambda u:u)
  971. xx, yy, zz, uu, vv = s.get_data()
  972. col = s.eval_color_func(xx, yy, zz, uu, vv)
  973. assert np.allclose(uu, col)
  974. s = ParametricSurfaceSeries(1, x, y, (x, 0, 1), (y, 0, 1), adaptive=False,
  975. n1=10, n2=10, color_func=lambda u, v: u * v)
  976. xx, yy, zz, uu, vv = s.get_data()
  977. col = s.eval_color_func(xx, yy, zz, uu, vv)
  978. assert np.allclose(uu * vv, col)
  979. s = ParametricSurfaceSeries(1, x, y, (x, 0, 1), (y, 0, 1), adaptive=False,
  980. n1=10, n2=10, color_func=lambda x, y, z: x * y * z)
  981. xx, yy, zz, uu, vv = s.get_data()
  982. col = s.eval_color_func(xx, yy, zz, uu, vv)
  983. assert np.allclose(xx * yy * zz, col)
  984. s = ParametricSurfaceSeries(1, x, y, (x, 0, 1), (y, 0, 1), adaptive=False,
  985. n1=10, n2=10, color_func=lambda x, y, z, u, v: x * y * z * u * v)
  986. xx, yy, zz, uu, vv = s.get_data()
  987. col = s.eval_color_func(xx, yy, zz, uu, vv)
  988. assert np.allclose(xx * yy * zz * uu * vv, col)
  989. # Interactive Series
  990. s = List2DSeries([0, 1, 2, x], [x, 2, 3, 4],
  991. color_func=lambda x, y: 2 * x, params={x: 1}, use_cm=True)
  992. xx, yy, col = s.get_data()
  993. assert np.allclose(xx, [0, 1, 2, 1])
  994. assert np.allclose(yy, [1, 2, 3, 4])
  995. assert np.allclose(2 * xx, col)
  996. assert s.is_parametric and s.use_cm
  997. s = List2DSeries([0, 1, 2, x], [x, 2, 3, 4],
  998. color_func=lambda x, y: 2 * x, params={x: 1}, use_cm=False)
  999. assert len(s.get_data()) == 2
  1000. assert not s.is_parametric
  1001. def test_color_func_scalar_val():
  1002. # verify that eval_color_func returns a numpy array even when color_func
  1003. # evaluates to a scalar value
  1004. if not np:
  1005. skip("numpy not installed.")
  1006. x, y = symbols("x, y")
  1007. s = Parametric2DLineSeries(cos(x), sin(x), (x, 0, 2*pi),
  1008. adaptive=False, n=10, color_func=lambda t: 1)
  1009. xx, yy, col = s.get_data()
  1010. assert np.allclose(col, np.ones(xx.shape))
  1011. s = Parametric3DLineSeries(cos(x), sin(x), x, (x, 0, 2*pi),
  1012. adaptive=False, n=10, color_func=lambda t: 1)
  1013. xx, yy, zz, col = s.get_data()
  1014. assert np.allclose(col, np.ones(xx.shape))
  1015. s = SurfaceOver2DRangeSeries(cos(x**2 + y**2), (x, -2, 2), (y, -2, 2),
  1016. adaptive=False, n1=10, n2=10, color_func=lambda x: 1)
  1017. xx, yy, zz = s.get_data()
  1018. assert np.allclose(s.eval_color_func(xx), np.ones(xx.shape))
  1019. s = ParametricSurfaceSeries(1, x, y, (x, 0, 1), (y, 0, 1), adaptive=False,
  1020. n1=10, n2=10, color_func=lambda u: 1)
  1021. xx, yy, zz, uu, vv = s.get_data()
  1022. col = s.eval_color_func(xx, yy, zz, uu, vv)
  1023. assert np.allclose(col, np.ones(xx.shape))
  1024. def test_color_func_expression():
  1025. # verify that color_func is able to deal with instances of Expr: they will
  1026. # be lambdified with the same signature used for the main expression.
  1027. if not np:
  1028. skip("numpy not installed.")
  1029. x, y = symbols("x, y")
  1030. s1 = Parametric2DLineSeries(cos(x), sin(x), (x, 0, 2*pi),
  1031. color_func=sin(x), adaptive=False, n=10, use_cm=True)
  1032. s2 = Parametric2DLineSeries(cos(x), sin(x), (x, 0, 2*pi),
  1033. color_func=lambda x: np.cos(x), adaptive=False, n=10, use_cm=True)
  1034. # the following statement should not raise errors
  1035. d1 = s1.get_data()
  1036. assert callable(s1.color_func)
  1037. d2 = s2.get_data()
  1038. assert not np.allclose(d1[-1], d2[-1])
  1039. s = SurfaceOver2DRangeSeries(cos(x**2 + y**2), (x, -pi, pi), (y, -pi, pi),
  1040. color_func=sin(x**2 + y**2), adaptive=False, n1=5, n2=5)
  1041. # the following statement should not raise errors
  1042. s.get_data()
  1043. assert callable(s.color_func)
  1044. xx = [1, 2, 3, 4, 5]
  1045. yy = [1, 2, 3, 4, 5]
  1046. raises(TypeError,
  1047. lambda : List2DSeries(xx, yy, use_cm=True, color_func=sin(x)))
  1048. def test_line_surface_color():
  1049. # verify the back-compatibility with the old sympy.plotting module.
  1050. # By setting line_color or surface_color to be a callable, it will set
  1051. # the color_func attribute.
  1052. x, y, z = symbols("x, y, z")
  1053. s = LineOver1DRangeSeries(sin(x), (x, -5, 5), adaptive=False, n=10,
  1054. line_color=lambda x: x)
  1055. assert (s.line_color is None) and callable(s.color_func)
  1056. s = Parametric2DLineSeries(cos(x), sin(x), (x, 0, 2*pi),
  1057. adaptive=False, n=10, line_color=lambda t: t)
  1058. assert (s.line_color is None) and callable(s.color_func)
  1059. s = SurfaceOver2DRangeSeries(cos(x**2 + y**2), (x, -2, 2), (y, -2, 2),
  1060. n1=10, n2=10, surface_color=lambda x: x)
  1061. assert (s.surface_color is None) and callable(s.color_func)
  1062. def test_complex_adaptive_false():
  1063. # verify that series with adaptive=False is evaluated with discretized
  1064. # ranges of type complex.
  1065. if not np:
  1066. skip("numpy not installed.")
  1067. x, y, u = symbols("x y u")
  1068. def do_test(data1, data2):
  1069. assert len(data1) == len(data2)
  1070. for d1, d2 in zip(data1, data2):
  1071. assert np.allclose(d1, d2)
  1072. expr1 = sqrt(x) * exp(-x**2)
  1073. expr2 = sqrt(u * x) * exp(-x**2)
  1074. s1 = LineOver1DRangeSeries(im(expr1), (x, -5, 5), adaptive=False, n=10)
  1075. s2 = LineOver1DRangeSeries(im(expr2), (x, -5, 5),
  1076. adaptive=False, n=10, params={u: 1})
  1077. data1 = s1.get_data()
  1078. data2 = s2.get_data()
  1079. do_test(data1, data2)
  1080. assert (not np.allclose(data1[1], 0)) and (not np.allclose(data2[1], 0))
  1081. s1 = Parametric2DLineSeries(re(expr1), im(expr1), (x, -pi, pi),
  1082. adaptive=False, n=10)
  1083. s2 = Parametric2DLineSeries(re(expr2), im(expr2), (x, -pi, pi),
  1084. adaptive=False, n=10, params={u: 1})
  1085. data1 = s1.get_data()
  1086. data2 = s2.get_data()
  1087. do_test(data1, data2)
  1088. assert (not np.allclose(data1[1], 0)) and (not np.allclose(data2[1], 0))
  1089. s1 = SurfaceOver2DRangeSeries(im(expr1), (x, -5, 5), (y, -10, 10),
  1090. adaptive=False, n1=30, n2=3)
  1091. s2 = SurfaceOver2DRangeSeries(im(expr2), (x, -5, 5), (y, -10, 10),
  1092. adaptive=False, n1=30, n2=3, params={u: 1})
  1093. data1 = s1.get_data()
  1094. data2 = s2.get_data()
  1095. do_test(data1, data2)
  1096. assert (not np.allclose(data1[1], 0)) and (not np.allclose(data2[1], 0))
  1097. def test_expr_is_lambda_function():
  1098. # verify that when a numpy function is provided, the series will be able
  1099. # to evaluate it. Also, label should be empty in order to prevent some
  1100. # backend from crashing.
  1101. if not np:
  1102. skip("numpy not installed.")
  1103. f = lambda x: np.cos(x)
  1104. s1 = LineOver1DRangeSeries(f, ("x", -5, 5), adaptive=True, depth=3)
  1105. s1.get_data()
  1106. s2 = LineOver1DRangeSeries(f, ("x", -5, 5), adaptive=False, n=10)
  1107. s2.get_data()
  1108. assert s1.label == s2.label == ""
  1109. fx = lambda x: np.cos(x)
  1110. fy = lambda x: np.sin(x)
  1111. s1 = Parametric2DLineSeries(fx, fy, ("x", 0, 2*pi),
  1112. adaptive=True, adaptive_goal=0.1)
  1113. s1.get_data()
  1114. s2 = Parametric2DLineSeries(fx, fy, ("x", 0, 2*pi),
  1115. adaptive=False, n=10)
  1116. s2.get_data()
  1117. assert s1.label == s2.label == ""
  1118. fz = lambda x: x
  1119. s1 = Parametric3DLineSeries(fx, fy, fz, ("x", 0, 2*pi),
  1120. adaptive=True, adaptive_goal=0.1)
  1121. s1.get_data()
  1122. s2 = Parametric3DLineSeries(fx, fy, fz, ("x", 0, 2*pi),
  1123. adaptive=False, n=10)
  1124. s2.get_data()
  1125. assert s1.label == s2.label == ""
  1126. f = lambda x, y: np.cos(x**2 + y**2)
  1127. s1 = SurfaceOver2DRangeSeries(f, ("a", -2, 2), ("b", -3, 3),
  1128. adaptive=False, n1=10, n2=10)
  1129. s1.get_data()
  1130. s2 = ContourSeries(f, ("a", -2, 2), ("b", -3, 3),
  1131. adaptive=False, n1=10, n2=10)
  1132. s2.get_data()
  1133. assert s1.label == s2.label == ""
  1134. fx = lambda u, v: np.cos(u + v)
  1135. fy = lambda u, v: np.sin(u - v)
  1136. fz = lambda u, v: u * v
  1137. s1 = ParametricSurfaceSeries(fx, fy, fz, ("u", 0, pi), ("v", 0, 2*pi),
  1138. adaptive=False, n1=10, n2=10)
  1139. s1.get_data()
  1140. assert s1.label == ""
  1141. raises(TypeError, lambda: List2DSeries(lambda t: t, lambda t: t))
  1142. raises(TypeError, lambda : ImplicitSeries(lambda t: np.sin(t),
  1143. ("x", -5, 5), ("y", -6, 6)))
  1144. def test_show_in_legend_lines():
  1145. # verify that lines series correctly set the show_in_legend attribute
  1146. x, u = symbols("x, u")
  1147. s = LineOver1DRangeSeries(cos(x), (x, -2, 2), "test", show_in_legend=True)
  1148. assert s.show_in_legend
  1149. s = LineOver1DRangeSeries(cos(x), (x, -2, 2), "test", show_in_legend=False)
  1150. assert not s.show_in_legend
  1151. s = Parametric2DLineSeries(cos(x), sin(x), (x, 0, 1), "test",
  1152. show_in_legend=True)
  1153. assert s.show_in_legend
  1154. s = Parametric2DLineSeries(cos(x), sin(x), (x, 0, 1), "test",
  1155. show_in_legend=False)
  1156. assert not s.show_in_legend
  1157. s = Parametric3DLineSeries(cos(x), sin(x), x, (x, 0, 1), "test",
  1158. show_in_legend=True)
  1159. assert s.show_in_legend
  1160. s = Parametric3DLineSeries(cos(x), sin(x), x, (x, 0, 1), "test",
  1161. show_in_legend=False)
  1162. assert not s.show_in_legend
  1163. @XFAIL
  1164. def test_particular_case_1_with_adaptive_true():
  1165. # Verify that symbolic expressions and numerical lambda functions are
  1166. # evaluated with the same algorithm.
  1167. if not np:
  1168. skip("numpy not installed.")
  1169. # NOTE: xfail because sympy's adaptive algorithm is not deterministic
  1170. def do_test(a, b):
  1171. with warns(
  1172. RuntimeWarning,
  1173. match="invalid value encountered in scalar power",
  1174. test_stacklevel=False,
  1175. ):
  1176. d1 = a.get_data()
  1177. d2 = b.get_data()
  1178. for t, v in zip(d1, d2):
  1179. assert np.allclose(t, v)
  1180. n = symbols("n")
  1181. a = S(2) / 3
  1182. epsilon = 0.01
  1183. xn = (n**3 + n**2)**(S(1)/3) - (n**3 - n**2)**(S(1)/3)
  1184. expr = Abs(xn - a) - epsilon
  1185. math_func = lambdify([n], expr)
  1186. s1 = LineOver1DRangeSeries(expr, (n, -10, 10), "",
  1187. adaptive=True, depth=3)
  1188. s2 = LineOver1DRangeSeries(math_func, ("n", -10, 10), "",
  1189. adaptive=True, depth=3)
  1190. do_test(s1, s2)
  1191. def test_particular_case_1_with_adaptive_false():
  1192. # Verify that symbolic expressions and numerical lambda functions are
  1193. # evaluated with the same algorithm. In particular, uniform evaluation
  1194. # is going to use np.vectorize, which correctly evaluates the following
  1195. # mathematical function.
  1196. if not np:
  1197. skip("numpy not installed.")
  1198. def do_test(a, b):
  1199. d1 = a.get_data()
  1200. d2 = b.get_data()
  1201. for t, v in zip(d1, d2):
  1202. assert np.allclose(t, v)
  1203. n = symbols("n")
  1204. a = S(2) / 3
  1205. epsilon = 0.01
  1206. xn = (n**3 + n**2)**(S(1)/3) - (n**3 - n**2)**(S(1)/3)
  1207. expr = Abs(xn - a) - epsilon
  1208. math_func = lambdify([n], expr)
  1209. s3 = LineOver1DRangeSeries(expr, (n, -10, 10), "",
  1210. adaptive=False, n=10)
  1211. s4 = LineOver1DRangeSeries(math_func, ("n", -10, 10), "",
  1212. adaptive=False, n=10)
  1213. do_test(s3, s4)
  1214. def test_complex_params_number_eval():
  1215. # The main expression contains terms like sqrt(xi - 1), with
  1216. # parameter (0 <= xi <= 1).
  1217. # There shouldn't be any NaN values on the output.
  1218. if not np:
  1219. skip("numpy not installed.")
  1220. xi, wn, x0, v0, t = symbols("xi, omega_n, x0, v0, t")
  1221. x = Function("x")(t)
  1222. eq = x.diff(t, 2) + 2 * xi * wn * x.diff(t) + wn**2 * x
  1223. sol = dsolve(eq, x, ics={x.subs(t, 0): x0, x.diff(t).subs(t, 0): v0})
  1224. params = {
  1225. wn: 0.5,
  1226. xi: 0.25,
  1227. x0: 0.45,
  1228. v0: 0.0
  1229. }
  1230. s = LineOver1DRangeSeries(sol.rhs, (t, 0, 100), adaptive=False, n=5,
  1231. params=params)
  1232. x, y = s.get_data()
  1233. assert not np.isnan(x).any()
  1234. assert not np.isnan(y).any()
  1235. # Fourier Series of a sawtooth wave
  1236. # The main expression contains a Sum with a symbolic upper range.
  1237. # The lambdified code looks like:
  1238. # sum(blablabla for for n in range(1, m+1))
  1239. # But range requires integer numbers, whereas per above example, the series
  1240. # casts parameters to complex. Verify that the series is able to detect
  1241. # upper bounds in summations and cast it to int in order to get successful
  1242. # evaluation
  1243. x, T, n, m = symbols("x, T, n, m")
  1244. fs = S(1) / 2 - (1 / pi) * Sum(sin(2 * n * pi * x / T) / n, (n, 1, m))
  1245. params = {
  1246. T: 4.5,
  1247. m: 5
  1248. }
  1249. s = LineOver1DRangeSeries(fs, (x, 0, 10), adaptive=False, n=5,
  1250. params=params)
  1251. x, y = s.get_data()
  1252. assert not np.isnan(x).any()
  1253. assert not np.isnan(y).any()
  1254. def test_complex_range_line_plot_1():
  1255. # verify that univariate functions are evaluated with a complex
  1256. # data range (with zero imaginary part). There shouldn't be any
  1257. # NaN value in the output.
  1258. if not np:
  1259. skip("numpy not installed.")
  1260. x, u = symbols("x, u")
  1261. expr1 = im(sqrt(x) * exp(-x**2))
  1262. expr2 = im(sqrt(u * x) * exp(-x**2))
  1263. s1 = LineOver1DRangeSeries(expr1, (x, -10, 10), adaptive=True,
  1264. adaptive_goal=0.1)
  1265. s2 = LineOver1DRangeSeries(expr1, (x, -10, 10), adaptive=False, n=30)
  1266. s3 = LineOver1DRangeSeries(expr2, (x, -10, 10), adaptive=False, n=30,
  1267. params={u: 1})
  1268. with ignore_warnings(RuntimeWarning):
  1269. data1 = s1.get_data()
  1270. data2 = s2.get_data()
  1271. data3 = s3.get_data()
  1272. assert not np.isnan(data1[1]).any()
  1273. assert not np.isnan(data2[1]).any()
  1274. assert not np.isnan(data3[1]).any()
  1275. assert np.allclose(data2[0], data3[0]) and np.allclose(data2[1], data3[1])
  1276. @XFAIL
  1277. def test_complex_range_line_plot_2():
  1278. # verify that univariate functions are evaluated with a complex
  1279. # data range (with non-zero imaginary part). There shouldn't be any
  1280. # NaN value in the output.
  1281. if not np:
  1282. skip("numpy not installed.")
  1283. # NOTE: xfail because sympy's adaptive algorithm is unable to deal with
  1284. # complex number.
  1285. x, u = symbols("x, u")
  1286. # adaptive and uniform meshing should produce the same data.
  1287. # because of the adaptive nature, just compare the first and last points
  1288. # of both series.
  1289. s1 = LineOver1DRangeSeries(abs(sqrt(x)), (x, -5-2j, 5-2j), adaptive=True)
  1290. s2 = LineOver1DRangeSeries(abs(sqrt(x)), (x, -5-2j, 5-2j), adaptive=False,
  1291. n=10)
  1292. with warns(
  1293. RuntimeWarning,
  1294. match="invalid value encountered in sqrt",
  1295. test_stacklevel=False,
  1296. ):
  1297. d1 = s1.get_data()
  1298. d2 = s2.get_data()
  1299. xx1 = [d1[0][0], d1[0][-1]]
  1300. xx2 = [d2[0][0], d2[0][-1]]
  1301. yy1 = [d1[1][0], d1[1][-1]]
  1302. yy2 = [d2[1][0], d2[1][-1]]
  1303. assert np.allclose(xx1, xx2)
  1304. assert np.allclose(yy1, yy2)
  1305. def test_force_real_eval():
  1306. # verify that force_real_eval=True produces inconsistent results when
  1307. # compared with evaluation of complex domain.
  1308. if not np:
  1309. skip("numpy not installed.")
  1310. x = symbols("x")
  1311. expr = im(sqrt(x) * exp(-x**2))
  1312. s1 = LineOver1DRangeSeries(expr, (x, -10, 10), adaptive=False, n=10,
  1313. force_real_eval=False)
  1314. s2 = LineOver1DRangeSeries(expr, (x, -10, 10), adaptive=False, n=10,
  1315. force_real_eval=True)
  1316. d1 = s1.get_data()
  1317. with ignore_warnings(RuntimeWarning):
  1318. d2 = s2.get_data()
  1319. assert not np.allclose(d1[1], 0)
  1320. assert np.allclose(d2[1], 0)
  1321. def test_contour_series_show_clabels():
  1322. # verify that a contour series has the abiliy to set the visibility of
  1323. # labels to contour lines
  1324. x, y = symbols("x, y")
  1325. s = ContourSeries(cos(x*y), (x, -2, 2), (y, -2, 2))
  1326. assert s.show_clabels
  1327. s = ContourSeries(cos(x*y), (x, -2, 2), (y, -2, 2), clabels=True)
  1328. assert s.show_clabels
  1329. s = ContourSeries(cos(x*y), (x, -2, 2), (y, -2, 2), clabels=False)
  1330. assert not s.show_clabels
  1331. def test_LineOver1DRangeSeries_complex_range():
  1332. # verify that LineOver1DRangeSeries can accept a complex range
  1333. # if the imaginary part of the start and end values are the same
  1334. x = symbols("x")
  1335. LineOver1DRangeSeries(sqrt(x), (x, -10, 10))
  1336. LineOver1DRangeSeries(sqrt(x), (x, -10-2j, 10-2j))
  1337. raises(ValueError,
  1338. lambda : LineOver1DRangeSeries(sqrt(x), (x, -10-2j, 10+2j)))
  1339. def test_symbolic_plotting_ranges():
  1340. # verify that data series can use symbolic plotting ranges
  1341. if not np:
  1342. skip("numpy not installed.")
  1343. x, y, z, a, b = symbols("x, y, z, a, b")
  1344. def do_test(s1, s2, new_params):
  1345. d1 = s1.get_data()
  1346. d2 = s2.get_data()
  1347. for u, v in zip(d1, d2):
  1348. assert np.allclose(u, v)
  1349. s2.params = new_params
  1350. d2 = s2.get_data()
  1351. for u, v in zip(d1, d2):
  1352. assert not np.allclose(u, v)
  1353. s1 = LineOver1DRangeSeries(sin(x), (x, 0, 1), adaptive=False, n=10)
  1354. s2 = LineOver1DRangeSeries(sin(x), (x, a, b), params={a: 0, b: 1},
  1355. adaptive=False, n=10)
  1356. do_test(s1, s2, {a: 0.5, b: 1.5})
  1357. # missing a parameter
  1358. raises(ValueError,
  1359. lambda : LineOver1DRangeSeries(sin(x), (x, a, b), params={a: 1}, n=10))
  1360. s1 = Parametric2DLineSeries(cos(x), sin(x), (x, 0, 1), adaptive=False, n=10)
  1361. s2 = Parametric2DLineSeries(cos(x), sin(x), (x, a, b), params={a: 0, b: 1},
  1362. adaptive=False, n=10)
  1363. do_test(s1, s2, {a: 0.5, b: 1.5})
  1364. # missing a parameter
  1365. raises(ValueError,
  1366. lambda : Parametric2DLineSeries(cos(x), sin(x), (x, a, b),
  1367. params={a: 0}, adaptive=False, n=10))
  1368. s1 = Parametric3DLineSeries(cos(x), sin(x), x, (x, 0, 1),
  1369. adaptive=False, n=10)
  1370. s2 = Parametric3DLineSeries(cos(x), sin(x), x, (x, a, b),
  1371. params={a: 0, b: 1}, adaptive=False, n=10)
  1372. do_test(s1, s2, {a: 0.5, b: 1.5})
  1373. # missing a parameter
  1374. raises(ValueError,
  1375. lambda : Parametric3DLineSeries(cos(x), sin(x), x, (x, a, b),
  1376. params={a: 0}, adaptive=False, n=10))
  1377. s1 = SurfaceOver2DRangeSeries(cos(x**2 + y**2), (x, -pi, pi), (y, -pi, pi),
  1378. adaptive=False, n1=5, n2=5)
  1379. s2 = SurfaceOver2DRangeSeries(cos(x**2 + y**2), (x, -pi * a, pi * a),
  1380. (y, -pi * b, pi * b), params={a: 1, b: 1},
  1381. adaptive=False, n1=5, n2=5)
  1382. do_test(s1, s2, {a: 0.5, b: 1.5})
  1383. # missing a parameter
  1384. raises(ValueError,
  1385. lambda : SurfaceOver2DRangeSeries(cos(x**2 + y**2),
  1386. (x, -pi * a, pi * a), (y, -pi * b, pi * b), params={a: 1},
  1387. adaptive=False, n1=5, n2=5))
  1388. # one range symbol is included into another range's minimum or maximum val
  1389. raises(ValueError,
  1390. lambda : SurfaceOver2DRangeSeries(cos(x**2 + y**2),
  1391. (x, -pi * a + y, pi * a), (y, -pi * b, pi * b), params={a: 1},
  1392. adaptive=False, n1=5, n2=5))
  1393. s1 = ParametricSurfaceSeries(
  1394. cos(x - y), sin(x + y), x - y, (x, -2, 2), (y, -2, 2), n1=5, n2=5)
  1395. s2 = ParametricSurfaceSeries(
  1396. cos(x - y), sin(x + y), x - y, (x, -2 * a, 2), (y, -2, 2 * b),
  1397. params={a: 1, b: 1}, n1=5, n2=5)
  1398. do_test(s1, s2, {a: 0.5, b: 1.5})
  1399. # missing a parameter
  1400. raises(ValueError,
  1401. lambda : ParametricSurfaceSeries(
  1402. cos(x - y), sin(x + y), x - y, (x, -2 * a, 2), (y, -2, 2 * b),
  1403. params={a: 1}, n1=5, n2=5))
  1404. def test_exclude_points():
  1405. # verify that exclude works as expected
  1406. if not np:
  1407. skip("numpy not installed.")
  1408. x = symbols("x")
  1409. expr = (floor(x) + S.Half) / (1 - (x - S.Half)**2)
  1410. with warns(
  1411. UserWarning,
  1412. match="NumPy is unable to evaluate with complex numbers some",
  1413. test_stacklevel=False,
  1414. ):
  1415. s = LineOver1DRangeSeries(expr, (x, -3.5, 3.5), adaptive=False, n=100,
  1416. exclude=list(range(-3, 4)))
  1417. xx, yy = s.get_data()
  1418. assert not np.isnan(xx).any()
  1419. assert np.count_nonzero(np.isnan(yy)) == 7
  1420. assert len(xx) > 100
  1421. e1 = log(floor(x)) * cos(x)
  1422. e2 = log(floor(x)) * sin(x)
  1423. with warns(
  1424. UserWarning,
  1425. match="NumPy is unable to evaluate with complex numbers some",
  1426. test_stacklevel=False,
  1427. ):
  1428. s = Parametric2DLineSeries(e1, e2, (x, 1, 12), adaptive=False, n=100,
  1429. exclude=list(range(1, 13)))
  1430. xx, yy, pp = s.get_data()
  1431. assert not np.isnan(pp).any()
  1432. assert np.count_nonzero(np.isnan(xx)) == 11
  1433. assert np.count_nonzero(np.isnan(yy)) == 11
  1434. assert len(xx) > 100
  1435. def test_unwrap():
  1436. # verify that unwrap works as expected
  1437. if not np:
  1438. skip("numpy not installed.")
  1439. x, y = symbols("x, y")
  1440. expr = 1 / (x**3 + 2*x**2 + x)
  1441. expr = arg(expr.subs(x, I*y*2*pi))
  1442. s1 = LineOver1DRangeSeries(expr, (y, 1e-05, 1e05), xscale="log",
  1443. adaptive=False, n=10, unwrap=False)
  1444. s2 = LineOver1DRangeSeries(expr, (y, 1e-05, 1e05), xscale="log",
  1445. adaptive=False, n=10, unwrap=True)
  1446. s3 = LineOver1DRangeSeries(expr, (y, 1e-05, 1e05), xscale="log",
  1447. adaptive=False, n=10, unwrap={"period": 4})
  1448. x1, y1 = s1.get_data()
  1449. x2, y2 = s2.get_data()
  1450. x3, y3 = s3.get_data()
  1451. assert np.allclose(x1, x2)
  1452. # there must not be nan values in the results of these evaluations
  1453. assert all(not np.isnan(t).any() for t in [y1, y2, y3])
  1454. assert not np.allclose(y1, y2)
  1455. assert not np.allclose(y1, y3)
  1456. assert not np.allclose(y2, y3)