test_plot.py 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344
  1. import os
  2. from tempfile import TemporaryDirectory
  3. import pytest
  4. from sympy.concrete.summations import Sum
  5. from sympy.core.numbers import (I, oo, pi)
  6. from sympy.core.relational import Ne
  7. from sympy.core.symbol import Symbol, symbols
  8. from sympy.functions.elementary.exponential import (LambertW, exp, exp_polar, log)
  9. from sympy.functions.elementary.miscellaneous import (real_root, sqrt)
  10. from sympy.functions.elementary.piecewise import Piecewise
  11. from sympy.functions.elementary.trigonometric import (cos, sin)
  12. from sympy.functions.elementary.miscellaneous import Min
  13. from sympy.functions.special.hyper import meijerg
  14. from sympy.integrals.integrals import Integral
  15. from sympy.logic.boolalg import And
  16. from sympy.core.singleton import S
  17. from sympy.core.sympify import sympify
  18. from sympy.external import import_module
  19. from sympy.plotting.plot import (
  20. Plot, plot, plot_parametric, plot3d_parametric_line, plot3d,
  21. plot3d_parametric_surface)
  22. from sympy.plotting.plot import (
  23. unset_show, plot_contour, PlotGrid, MatplotlibBackend, TextBackend)
  24. from sympy.plotting.series import (
  25. LineOver1DRangeSeries, Parametric2DLineSeries, Parametric3DLineSeries,
  26. ParametricSurfaceSeries, SurfaceOver2DRangeSeries)
  27. from sympy.testing.pytest import skip, skip_under_pyodide, warns, raises, warns_deprecated_sympy
  28. from sympy.utilities import lambdify as lambdify_
  29. from sympy.utilities.exceptions import ignore_warnings
  30. unset_show()
  31. matplotlib = import_module(
  32. 'matplotlib', min_module_version='1.1.0', catch=(RuntimeError,))
  33. class DummyBackendNotOk(Plot):
  34. """ Used to verify if users can create their own backends.
  35. This backend is meant to raise NotImplementedError for methods `show`,
  36. `save`, `close`.
  37. """
  38. def __new__(cls, *args, **kwargs):
  39. return object.__new__(cls)
  40. class DummyBackendOk(Plot):
  41. """ Used to verify if users can create their own backends.
  42. This backend is meant to pass all tests.
  43. """
  44. def __new__(cls, *args, **kwargs):
  45. return object.__new__(cls)
  46. def show(self):
  47. pass
  48. def save(self):
  49. pass
  50. def close(self):
  51. pass
  52. def test_basic_plotting_backend():
  53. x = Symbol('x')
  54. plot(x, (x, 0, 3), backend='text')
  55. plot(x**2 + 1, (x, 0, 3), backend='text')
  56. @pytest.mark.parametrize("adaptive", [True, False])
  57. def test_plot_and_save_1(adaptive):
  58. if not matplotlib:
  59. skip("Matplotlib not the default backend")
  60. x = Symbol('x')
  61. y = Symbol('y')
  62. with TemporaryDirectory(prefix='sympy_') as tmpdir:
  63. ###
  64. # Examples from the 'introduction' notebook
  65. ###
  66. p = plot(x, legend=True, label='f1', adaptive=adaptive, n=10)
  67. p = plot(x*sin(x), x*cos(x), label='f2', adaptive=adaptive, n=10)
  68. p.extend(p)
  69. p[0].line_color = lambda a: a
  70. p[1].line_color = 'b'
  71. p.title = 'Big title'
  72. p.xlabel = 'the x axis'
  73. p[1].label = 'straight line'
  74. p.legend = True
  75. p.aspect_ratio = (1, 1)
  76. p.xlim = (-15, 20)
  77. filename = 'test_basic_options_and_colors.png'
  78. p.save(os.path.join(tmpdir, filename))
  79. p._backend.close()
  80. p.extend(plot(x + 1, adaptive=adaptive, n=10))
  81. p.append(plot(x + 3, x**2, adaptive=adaptive, n=10)[1])
  82. filename = 'test_plot_extend_append.png'
  83. p.save(os.path.join(tmpdir, filename))
  84. p[2] = plot(x**2, (x, -2, 3), adaptive=adaptive, n=10)
  85. filename = 'test_plot_setitem.png'
  86. p.save(os.path.join(tmpdir, filename))
  87. p._backend.close()
  88. p = plot(sin(x), (x, -2*pi, 4*pi), adaptive=adaptive, n=10)
  89. filename = 'test_line_explicit.png'
  90. p.save(os.path.join(tmpdir, filename))
  91. p._backend.close()
  92. p = plot(sin(x), adaptive=adaptive, n=10)
  93. filename = 'test_line_default_range.png'
  94. p.save(os.path.join(tmpdir, filename))
  95. p._backend.close()
  96. p = plot((x**2, (x, -5, 5)), (x**3, (x, -3, 3)), adaptive=adaptive, n=10)
  97. filename = 'test_line_multiple_range.png'
  98. p.save(os.path.join(tmpdir, filename))
  99. p._backend.close()
  100. raises(ValueError, lambda: plot(x, y))
  101. #Piecewise plots
  102. p = plot(Piecewise((1, x > 0), (0, True)), (x, -1, 1), adaptive=adaptive, n=10)
  103. filename = 'test_plot_piecewise.png'
  104. p.save(os.path.join(tmpdir, filename))
  105. p._backend.close()
  106. p = plot(Piecewise((x, x < 1), (x**2, True)), (x, -3, 3), adaptive=adaptive, n=10)
  107. filename = 'test_plot_piecewise_2.png'
  108. p.save(os.path.join(tmpdir, filename))
  109. p._backend.close()
  110. # test issue 7471
  111. p1 = plot(x, adaptive=adaptive, n=10)
  112. p2 = plot(3, adaptive=adaptive, n=10)
  113. p1.extend(p2)
  114. filename = 'test_horizontal_line.png'
  115. p.save(os.path.join(tmpdir, filename))
  116. p._backend.close()
  117. # test issue 10925
  118. f = Piecewise((-1, x < -1), (x, And(-1 <= x, x < 0)), \
  119. (x**2, And(0 <= x, x < 1)), (x**3, x >= 1))
  120. p = plot(f, (x, -3, 3), adaptive=adaptive, n=10)
  121. filename = 'test_plot_piecewise_3.png'
  122. p.save(os.path.join(tmpdir, filename))
  123. p._backend.close()
  124. @pytest.mark.parametrize("adaptive", [True, False])
  125. def test_plot_and_save_2(adaptive):
  126. if not matplotlib:
  127. skip("Matplotlib not the default backend")
  128. x = Symbol('x')
  129. y = Symbol('y')
  130. z = Symbol('z')
  131. with TemporaryDirectory(prefix='sympy_') as tmpdir:
  132. #parametric 2d plots.
  133. #Single plot with default range.
  134. p = plot_parametric(sin(x), cos(x), adaptive=adaptive, n=10)
  135. filename = 'test_parametric.png'
  136. p.save(os.path.join(tmpdir, filename))
  137. p._backend.close()
  138. #Single plot with range.
  139. p = plot_parametric(
  140. sin(x), cos(x), (x, -5, 5), legend=True, label='parametric_plot',
  141. adaptive=adaptive, n=10)
  142. filename = 'test_parametric_range.png'
  143. p.save(os.path.join(tmpdir, filename))
  144. p._backend.close()
  145. #Multiple plots with same range.
  146. p = plot_parametric((sin(x), cos(x)), (x, sin(x)),
  147. adaptive=adaptive, n=10)
  148. filename = 'test_parametric_multiple.png'
  149. p.save(os.path.join(tmpdir, filename))
  150. p._backend.close()
  151. #Multiple plots with different ranges.
  152. p = plot_parametric(
  153. (sin(x), cos(x), (x, -3, 3)), (x, sin(x), (x, -5, 5)),
  154. adaptive=adaptive, n=10)
  155. filename = 'test_parametric_multiple_ranges.png'
  156. p.save(os.path.join(tmpdir, filename))
  157. p._backend.close()
  158. #depth of recursion specified.
  159. p = plot_parametric(x, sin(x), depth=13,
  160. adaptive=adaptive, n=10)
  161. filename = 'test_recursion_depth.png'
  162. p.save(os.path.join(tmpdir, filename))
  163. p._backend.close()
  164. #No adaptive sampling.
  165. p = plot_parametric(cos(x), sin(x), adaptive=False, n=500)
  166. filename = 'test_adaptive.png'
  167. p.save(os.path.join(tmpdir, filename))
  168. p._backend.close()
  169. #3d parametric plots
  170. p = plot3d_parametric_line(
  171. sin(x), cos(x), x, legend=True, label='3d_parametric_plot',
  172. adaptive=adaptive, n=10)
  173. filename = 'test_3d_line.png'
  174. p.save(os.path.join(tmpdir, filename))
  175. p._backend.close()
  176. p = plot3d_parametric_line(
  177. (sin(x), cos(x), x, (x, -5, 5)), (cos(x), sin(x), x, (x, -3, 3)),
  178. adaptive=adaptive, n=10)
  179. filename = 'test_3d_line_multiple.png'
  180. p.save(os.path.join(tmpdir, filename))
  181. p._backend.close()
  182. p = plot3d_parametric_line(sin(x), cos(x), x, n=30,
  183. adaptive=adaptive)
  184. filename = 'test_3d_line_points.png'
  185. p.save(os.path.join(tmpdir, filename))
  186. p._backend.close()
  187. # 3d surface single plot.
  188. p = plot3d(x * y, adaptive=adaptive, n=10)
  189. filename = 'test_surface.png'
  190. p.save(os.path.join(tmpdir, filename))
  191. p._backend.close()
  192. # Multiple 3D plots with same range.
  193. p = plot3d(-x * y, x * y, (x, -5, 5), adaptive=adaptive, n=10)
  194. filename = 'test_surface_multiple.png'
  195. p.save(os.path.join(tmpdir, filename))
  196. p._backend.close()
  197. # Multiple 3D plots with different ranges.
  198. p = plot3d(
  199. (x * y, (x, -3, 3), (y, -3, 3)), (-x * y, (x, -3, 3), (y, -3, 3)),
  200. adaptive=adaptive, n=10)
  201. filename = 'test_surface_multiple_ranges.png'
  202. p.save(os.path.join(tmpdir, filename))
  203. p._backend.close()
  204. # Single Parametric 3D plot
  205. p = plot3d_parametric_surface(sin(x + y), cos(x - y), x - y,
  206. adaptive=adaptive, n=10)
  207. filename = 'test_parametric_surface.png'
  208. p.save(os.path.join(tmpdir, filename))
  209. p._backend.close()
  210. # Multiple Parametric 3D plots.
  211. p = plot3d_parametric_surface(
  212. (x*sin(z), x*cos(z), z, (x, -5, 5), (z, -5, 5)),
  213. (sin(x + y), cos(x - y), x - y, (x, -5, 5), (y, -5, 5)),
  214. adaptive=adaptive, n=10)
  215. filename = 'test_parametric_surface.png'
  216. p.save(os.path.join(tmpdir, filename))
  217. p._backend.close()
  218. # Single Contour plot.
  219. p = plot_contour(sin(x)*sin(y), (x, -5, 5), (y, -5, 5),
  220. adaptive=adaptive, n=10)
  221. filename = 'test_contour_plot.png'
  222. p.save(os.path.join(tmpdir, filename))
  223. p._backend.close()
  224. # Multiple Contour plots with same range.
  225. p = plot_contour(x**2 + y**2, x**3 + y**3, (x, -5, 5), (y, -5, 5),
  226. adaptive=adaptive, n=10)
  227. filename = 'test_contour_plot.png'
  228. p.save(os.path.join(tmpdir, filename))
  229. p._backend.close()
  230. # Multiple Contour plots with different range.
  231. p = plot_contour(
  232. (x**2 + y**2, (x, -5, 5), (y, -5, 5)),
  233. (x**3 + y**3, (x, -3, 3), (y, -3, 3)),
  234. adaptive=adaptive, n=10)
  235. filename = 'test_contour_plot.png'
  236. p.save(os.path.join(tmpdir, filename))
  237. p._backend.close()
  238. @pytest.mark.parametrize("adaptive", [True, False])
  239. def test_plot_and_save_3(adaptive):
  240. if not matplotlib:
  241. skip("Matplotlib not the default backend")
  242. x = Symbol('x')
  243. y = Symbol('y')
  244. z = Symbol('z')
  245. with TemporaryDirectory(prefix='sympy_') as tmpdir:
  246. ###
  247. # Examples from the 'colors' notebook
  248. ###
  249. p = plot(sin(x), adaptive=adaptive, n=10)
  250. p[0].line_color = lambda a: a
  251. filename = 'test_colors_line_arity1.png'
  252. p.save(os.path.join(tmpdir, filename))
  253. p[0].line_color = lambda a, b: b
  254. filename = 'test_colors_line_arity2.png'
  255. p.save(os.path.join(tmpdir, filename))
  256. p._backend.close()
  257. p = plot(x*sin(x), x*cos(x), (x, 0, 10), adaptive=adaptive, n=10)
  258. p[0].line_color = lambda a: a
  259. filename = 'test_colors_param_line_arity1.png'
  260. p.save(os.path.join(tmpdir, filename))
  261. p[0].line_color = lambda a, b: a
  262. filename = 'test_colors_param_line_arity1.png'
  263. p.save(os.path.join(tmpdir, filename))
  264. p[0].line_color = lambda a, b: b
  265. filename = 'test_colors_param_line_arity2b.png'
  266. p.save(os.path.join(tmpdir, filename))
  267. p._backend.close()
  268. p = plot3d_parametric_line(
  269. sin(x) + 0.1*sin(x)*cos(7*x),
  270. cos(x) + 0.1*cos(x)*cos(7*x),
  271. 0.1*sin(7*x),
  272. (x, 0, 2*pi), adaptive=adaptive, n=10)
  273. p[0].line_color = lambdify_(x, sin(4*x))
  274. filename = 'test_colors_3d_line_arity1.png'
  275. p.save(os.path.join(tmpdir, filename))
  276. p[0].line_color = lambda a, b: b
  277. filename = 'test_colors_3d_line_arity2.png'
  278. p.save(os.path.join(tmpdir, filename))
  279. p[0].line_color = lambda a, b, c: c
  280. filename = 'test_colors_3d_line_arity3.png'
  281. p.save(os.path.join(tmpdir, filename))
  282. p._backend.close()
  283. p = plot3d(sin(x)*y, (x, 0, 6*pi), (y, -5, 5), adaptive=adaptive, n=10)
  284. p[0].surface_color = lambda a: a
  285. filename = 'test_colors_surface_arity1.png'
  286. p.save(os.path.join(tmpdir, filename))
  287. p[0].surface_color = lambda a, b: b
  288. filename = 'test_colors_surface_arity2.png'
  289. p.save(os.path.join(tmpdir, filename))
  290. p[0].surface_color = lambda a, b, c: c
  291. filename = 'test_colors_surface_arity3a.png'
  292. p.save(os.path.join(tmpdir, filename))
  293. p[0].surface_color = lambdify_((x, y, z), sqrt((x - 3*pi)**2 + y**2))
  294. filename = 'test_colors_surface_arity3b.png'
  295. p.save(os.path.join(tmpdir, filename))
  296. p._backend.close()
  297. p = plot3d_parametric_surface(x * cos(4 * y), x * sin(4 * y), y,
  298. (x, -1, 1), (y, -1, 1), adaptive=adaptive, n=10)
  299. p[0].surface_color = lambda a: a
  300. filename = 'test_colors_param_surf_arity1.png'
  301. p.save(os.path.join(tmpdir, filename))
  302. p[0].surface_color = lambda a, b: a*b
  303. filename = 'test_colors_param_surf_arity2.png'
  304. p.save(os.path.join(tmpdir, filename))
  305. p[0].surface_color = lambdify_((x, y, z), sqrt(x**2 + y**2 + z**2))
  306. filename = 'test_colors_param_surf_arity3.png'
  307. p.save(os.path.join(tmpdir, filename))
  308. p._backend.close()
  309. @pytest.mark.parametrize("adaptive", [True])
  310. def test_plot_and_save_4(adaptive):
  311. if not matplotlib:
  312. skip("Matplotlib not the default backend")
  313. x = Symbol('x')
  314. y = Symbol('y')
  315. ###
  316. # Examples from the 'advanced' notebook
  317. ###
  318. with TemporaryDirectory(prefix='sympy_') as tmpdir:
  319. i = Integral(log((sin(x)**2 + 1)*sqrt(x**2 + 1)), (x, 0, y))
  320. p = plot(i, (y, 1, 5), adaptive=adaptive, n=10, force_real_eval=True)
  321. filename = 'test_advanced_integral.png'
  322. p.save(os.path.join(tmpdir, filename))
  323. p._backend.close()
  324. @pytest.mark.parametrize("adaptive", [True, False])
  325. def test_plot_and_save_5(adaptive):
  326. if not matplotlib:
  327. skip("Matplotlib not the default backend")
  328. x = Symbol('x')
  329. y = Symbol('y')
  330. with TemporaryDirectory(prefix='sympy_') as tmpdir:
  331. s = Sum(1/x**y, (x, 1, oo))
  332. p = plot(s, (y, 2, 10), adaptive=adaptive, n=10)
  333. filename = 'test_advanced_inf_sum.png'
  334. p.save(os.path.join(tmpdir, filename))
  335. p._backend.close()
  336. p = plot(Sum(1/x, (x, 1, y)), (y, 2, 10), show=False,
  337. adaptive=adaptive, n=10)
  338. p[0].only_integers = True
  339. p[0].steps = True
  340. filename = 'test_advanced_fin_sum.png'
  341. # XXX: This should be fixed in experimental_lambdify or by using
  342. # ordinary lambdify so that it doesn't warn. The error results from
  343. # passing an array of values as the integration limit.
  344. #
  345. # UserWarning: The evaluation of the expression is problematic. We are
  346. # trying a failback method that may still work. Please report this as a
  347. # bug.
  348. with ignore_warnings(UserWarning):
  349. p.save(os.path.join(tmpdir, filename))
  350. p._backend.close()
  351. @pytest.mark.parametrize("adaptive", [True, False])
  352. def test_plot_and_save_6(adaptive):
  353. if not matplotlib:
  354. skip("Matplotlib not the default backend")
  355. x = Symbol('x')
  356. with TemporaryDirectory(prefix='sympy_') as tmpdir:
  357. filename = 'test.png'
  358. ###
  359. # Test expressions that can not be translated to np and generate complex
  360. # results.
  361. ###
  362. p = plot(sin(x) + I*cos(x))
  363. p.save(os.path.join(tmpdir, filename))
  364. with ignore_warnings(RuntimeWarning):
  365. p = plot(sqrt(sqrt(-x)))
  366. p.save(os.path.join(tmpdir, filename))
  367. p = plot(LambertW(x))
  368. p.save(os.path.join(tmpdir, filename))
  369. p = plot(sqrt(LambertW(x)))
  370. p.save(os.path.join(tmpdir, filename))
  371. #Characteristic function of a StudentT distribution with nu=10
  372. x1 = 5 * x**2 * exp_polar(-I*pi)/2
  373. m1 = meijerg(((1 / 2,), ()), ((5, 0, 1 / 2), ()), x1)
  374. x2 = 5*x**2 * exp_polar(I*pi)/2
  375. m2 = meijerg(((1/2,), ()), ((5, 0, 1/2), ()), x2)
  376. expr = (m1 + m2) / (48 * pi)
  377. with warns(
  378. UserWarning,
  379. match="The evaluation with NumPy/SciPy failed",
  380. test_stacklevel=False,
  381. ):
  382. p = plot(expr, (x, 1e-6, 1e-2), adaptive=adaptive, n=10)
  383. p.save(os.path.join(tmpdir, filename))
  384. @pytest.mark.parametrize("adaptive", [True, False])
  385. def test_plotgrid_and_save(adaptive):
  386. if not matplotlib:
  387. skip("Matplotlib not the default backend")
  388. x = Symbol('x')
  389. y = Symbol('y')
  390. with TemporaryDirectory(prefix='sympy_') as tmpdir:
  391. p1 = plot(x, adaptive=adaptive, n=10)
  392. p2 = plot_parametric((sin(x), cos(x)), (x, sin(x)), show=False,
  393. adaptive=adaptive, n=10)
  394. p3 = plot_parametric(
  395. cos(x), sin(x), adaptive=adaptive, n=10, show=False)
  396. p4 = plot3d_parametric_line(sin(x), cos(x), x, show=False,
  397. adaptive=adaptive, n=10)
  398. # symmetric grid
  399. p = PlotGrid(2, 2, p1, p2, p3, p4)
  400. filename = 'test_grid1.png'
  401. p.save(os.path.join(tmpdir, filename))
  402. p._backend.close()
  403. # grid size greater than the number of subplots
  404. p = PlotGrid(3, 4, p1, p2, p3, p4)
  405. filename = 'test_grid2.png'
  406. p.save(os.path.join(tmpdir, filename))
  407. p._backend.close()
  408. p5 = plot(cos(x),(x, -pi, pi), show=False, adaptive=adaptive, n=10)
  409. p5[0].line_color = lambda a: a
  410. p6 = plot(Piecewise((1, x > 0), (0, True)), (x, -1, 1), show=False,
  411. adaptive=adaptive, n=10)
  412. p7 = plot_contour(
  413. (x**2 + y**2, (x, -5, 5), (y, -5, 5)),
  414. (x**3 + y**3, (x, -3, 3), (y, -3, 3)), show=False,
  415. adaptive=adaptive, n=10)
  416. # unsymmetric grid (subplots in one line)
  417. p = PlotGrid(1, 3, p5, p6, p7)
  418. filename = 'test_grid3.png'
  419. p.save(os.path.join(tmpdir, filename))
  420. p._backend.close()
  421. @pytest.mark.parametrize("adaptive", [True, False])
  422. def test_append_issue_7140(adaptive):
  423. if not matplotlib:
  424. skip("Matplotlib not the default backend")
  425. x = Symbol('x')
  426. p1 = plot(x, adaptive=adaptive, n=10)
  427. p2 = plot(x**2, adaptive=adaptive, n=10)
  428. plot(x + 2, adaptive=adaptive, n=10)
  429. # append a series
  430. p2.append(p1[0])
  431. assert len(p2._series) == 2
  432. with raises(TypeError):
  433. p1.append(p2)
  434. with raises(TypeError):
  435. p1.append(p2._series)
  436. @pytest.mark.parametrize("adaptive", [True, False])
  437. def test_issue_15265(adaptive):
  438. if not matplotlib:
  439. skip("Matplotlib not the default backend")
  440. x = Symbol('x')
  441. eqn = sin(x)
  442. p = plot(eqn, xlim=(-S.Pi, S.Pi), ylim=(-1, 1), adaptive=adaptive, n=10)
  443. p._backend.close()
  444. p = plot(eqn, xlim=(-1, 1), ylim=(-S.Pi, S.Pi), adaptive=adaptive, n=10)
  445. p._backend.close()
  446. p = plot(eqn, xlim=(-1, 1), adaptive=adaptive, n=10,
  447. ylim=(sympify('-3.14'), sympify('3.14')))
  448. p._backend.close()
  449. p = plot(eqn, adaptive=adaptive, n=10,
  450. xlim=(sympify('-3.14'), sympify('3.14')), ylim=(-1, 1))
  451. p._backend.close()
  452. raises(ValueError,
  453. lambda: plot(eqn, adaptive=adaptive, n=10,
  454. xlim=(-S.ImaginaryUnit, 1), ylim=(-1, 1)))
  455. raises(ValueError,
  456. lambda: plot(eqn, adaptive=adaptive, n=10,
  457. xlim=(-1, 1), ylim=(-1, S.ImaginaryUnit)))
  458. raises(ValueError,
  459. lambda: plot(eqn, adaptive=adaptive, n=10,
  460. xlim=(S.NegativeInfinity, 1), ylim=(-1, 1)))
  461. raises(ValueError,
  462. lambda: plot(eqn, adaptive=adaptive, n=10,
  463. xlim=(-1, 1), ylim=(-1, S.Infinity)))
  464. def test_empty_Plot():
  465. if not matplotlib:
  466. skip("Matplotlib not the default backend")
  467. # No exception showing an empty plot
  468. plot()
  469. # Plot is only a base class: doesn't implement any logic for showing
  470. # images
  471. p = Plot()
  472. raises(NotImplementedError, lambda: p.show())
  473. @pytest.mark.parametrize("adaptive", [True, False])
  474. def test_issue_17405(adaptive):
  475. if not matplotlib:
  476. skip("Matplotlib not the default backend")
  477. x = Symbol('x')
  478. f = x**0.3 - 10*x**3 + x**2
  479. p = plot(f, (x, -10, 10), adaptive=adaptive, n=30, show=False)
  480. # Random number of segments, probably more than 100, but we want to see
  481. # that there are segments generated, as opposed to when the bug was present
  482. # RuntimeWarning: invalid value encountered in double_scalars
  483. with ignore_warnings(RuntimeWarning):
  484. assert len(p[0].get_data()[0]) >= 30
  485. @pytest.mark.parametrize("adaptive", [True, False])
  486. def test_logplot_PR_16796(adaptive):
  487. if not matplotlib:
  488. skip("Matplotlib not the default backend")
  489. x = Symbol('x')
  490. p = plot(x, (x, .001, 100), adaptive=adaptive, n=30,
  491. xscale='log', show=False)
  492. # Random number of segments, probably more than 100, but we want to see
  493. # that there are segments generated, as opposed to when the bug was present
  494. assert len(p[0].get_data()[0]) >= 30
  495. assert p[0].end == 100.0
  496. assert p[0].start == .001
  497. @pytest.mark.parametrize("adaptive", [True, False])
  498. def test_issue_16572(adaptive):
  499. if not matplotlib:
  500. skip("Matplotlib not the default backend")
  501. x = Symbol('x')
  502. p = plot(LambertW(x), show=False, adaptive=adaptive, n=30)
  503. # Random number of segments, probably more than 50, but we want to see
  504. # that there are segments generated, as opposed to when the bug was present
  505. assert len(p[0].get_data()[0]) >= 30
  506. @pytest.mark.parametrize("adaptive", [True, False])
  507. def test_issue_11865(adaptive):
  508. if not matplotlib:
  509. skip("Matplotlib not the default backend")
  510. k = Symbol('k', integer=True)
  511. f = Piecewise((-I*exp(I*pi*k)/k + I*exp(-I*pi*k)/k, Ne(k, 0)), (2*pi, True))
  512. p = plot(f, show=False, adaptive=adaptive, n=30)
  513. # Random number of segments, probably more than 100, but we want to see
  514. # that there are segments generated, as opposed to when the bug was present
  515. # and that there are no exceptions.
  516. assert len(p[0].get_data()[0]) >= 30
  517. @skip_under_pyodide("Warnings not emitted in Pyodide because of lack of WASM fp exception support")
  518. def test_issue_11461():
  519. if not matplotlib:
  520. skip("Matplotlib not the default backend")
  521. x = Symbol('x')
  522. p = plot(real_root((log(x/(x-2))), 3), show=False, adaptive=True)
  523. with warns(
  524. RuntimeWarning,
  525. match="invalid value encountered in",
  526. test_stacklevel=False,
  527. ):
  528. # Random number of segments, probably more than 100, but we want to see
  529. # that there are segments generated, as opposed to when the bug was present
  530. # and that there are no exceptions.
  531. assert len(p[0].get_data()[0]) >= 30
  532. @pytest.mark.parametrize("adaptive", [True, False])
  533. def test_issue_11764(adaptive):
  534. if not matplotlib:
  535. skip("Matplotlib not the default backend")
  536. x = Symbol('x')
  537. p = plot_parametric(cos(x), sin(x), (x, 0, 2 * pi),
  538. aspect_ratio=(1,1), show=False, adaptive=adaptive, n=30)
  539. assert p.aspect_ratio == (1, 1)
  540. # Random number of segments, probably more than 100, but we want to see
  541. # that there are segments generated, as opposed to when the bug was present
  542. assert len(p[0].get_data()[0]) >= 30
  543. @pytest.mark.parametrize("adaptive", [True, False])
  544. def test_issue_13516(adaptive):
  545. if not matplotlib:
  546. skip("Matplotlib not the default backend")
  547. x = Symbol('x')
  548. pm = plot(sin(x), backend="matplotlib", show=False, adaptive=adaptive, n=30)
  549. assert pm.backend == MatplotlibBackend
  550. assert len(pm[0].get_data()[0]) >= 30
  551. pt = plot(sin(x), backend="text", show=False, adaptive=adaptive, n=30)
  552. assert pt.backend == TextBackend
  553. assert len(pt[0].get_data()[0]) >= 30
  554. pd = plot(sin(x), backend="default", show=False, adaptive=adaptive, n=30)
  555. assert pd.backend == MatplotlibBackend
  556. assert len(pd[0].get_data()[0]) >= 30
  557. p = plot(sin(x), show=False, adaptive=adaptive, n=30)
  558. assert p.backend == MatplotlibBackend
  559. assert len(p[0].get_data()[0]) >= 30
  560. @pytest.mark.parametrize("adaptive", [True, False])
  561. def test_plot_limits(adaptive):
  562. if not matplotlib:
  563. skip("Matplotlib not the default backend")
  564. x = Symbol('x')
  565. p = plot(x, x**2, (x, -10, 10), adaptive=adaptive, n=10)
  566. backend = p._backend
  567. xmin, xmax = backend.ax.get_xlim()
  568. assert abs(xmin + 10) < 2
  569. assert abs(xmax - 10) < 2
  570. ymin, ymax = backend.ax.get_ylim()
  571. assert abs(ymin + 10) < 10
  572. assert abs(ymax - 100) < 10
  573. @pytest.mark.parametrize("adaptive", [True, False])
  574. def test_plot3d_parametric_line_limits(adaptive):
  575. if not matplotlib:
  576. skip("Matplotlib not the default backend")
  577. x = Symbol('x')
  578. v1 = (2*cos(x), 2*sin(x), 2*x, (x, -5, 5))
  579. v2 = (sin(x), cos(x), x, (x, -5, 5))
  580. p = plot3d_parametric_line(v1, v2, adaptive=adaptive, n=60)
  581. backend = p._backend
  582. xmin, xmax = backend.ax.get_xlim()
  583. assert abs(xmin + 2) < 1e-2
  584. assert abs(xmax - 2) < 1e-2
  585. ymin, ymax = backend.ax.get_ylim()
  586. assert abs(ymin + 2) < 1e-2
  587. assert abs(ymax - 2) < 1e-2
  588. zmin, zmax = backend.ax.get_zlim()
  589. assert abs(zmin + 10) < 1e-2
  590. assert abs(zmax - 10) < 1e-2
  591. p = plot3d_parametric_line(v2, v1, adaptive=adaptive, n=60)
  592. backend = p._backend
  593. xmin, xmax = backend.ax.get_xlim()
  594. assert abs(xmin + 2) < 1e-2
  595. assert abs(xmax - 2) < 1e-2
  596. ymin, ymax = backend.ax.get_ylim()
  597. assert abs(ymin + 2) < 1e-2
  598. assert abs(ymax - 2) < 1e-2
  599. zmin, zmax = backend.ax.get_zlim()
  600. assert abs(zmin + 10) < 1e-2
  601. assert abs(zmax - 10) < 1e-2
  602. @pytest.mark.parametrize("adaptive", [True, False])
  603. def test_plot_size(adaptive):
  604. if not matplotlib:
  605. skip("Matplotlib not the default backend")
  606. x = Symbol('x')
  607. p1 = plot(sin(x), backend="matplotlib", size=(8, 4),
  608. adaptive=adaptive, n=10)
  609. s1 = p1._backend.fig.get_size_inches()
  610. assert (s1[0] == 8) and (s1[1] == 4)
  611. p2 = plot(sin(x), backend="matplotlib", size=(5, 10),
  612. adaptive=adaptive, n=10)
  613. s2 = p2._backend.fig.get_size_inches()
  614. assert (s2[0] == 5) and (s2[1] == 10)
  615. p3 = PlotGrid(2, 1, p1, p2, size=(6, 2),
  616. adaptive=adaptive, n=10)
  617. s3 = p3._backend.fig.get_size_inches()
  618. assert (s3[0] == 6) and (s3[1] == 2)
  619. with raises(ValueError):
  620. plot(sin(x), backend="matplotlib", size=(-1, 3))
  621. def test_issue_20113():
  622. if not matplotlib:
  623. skip("Matplotlib not the default backend")
  624. x = Symbol('x')
  625. # verify the capability to use custom backends
  626. plot(sin(x), backend=Plot, show=False)
  627. p2 = plot(sin(x), backend=MatplotlibBackend, show=False)
  628. assert p2.backend == MatplotlibBackend
  629. assert len(p2[0].get_data()[0]) >= 30
  630. p3 = plot(sin(x), backend=DummyBackendOk, show=False)
  631. assert p3.backend == DummyBackendOk
  632. assert len(p3[0].get_data()[0]) >= 30
  633. # test for an improper coded backend
  634. p4 = plot(sin(x), backend=DummyBackendNotOk, show=False)
  635. assert p4.backend == DummyBackendNotOk
  636. assert len(p4[0].get_data()[0]) >= 30
  637. with raises(NotImplementedError):
  638. p4.show()
  639. with raises(NotImplementedError):
  640. p4.save("test/path")
  641. with raises(NotImplementedError):
  642. p4._backend.close()
  643. def test_custom_coloring():
  644. x = Symbol('x')
  645. y = Symbol('y')
  646. plot(cos(x), line_color=lambda a: a)
  647. plot(cos(x), line_color=1)
  648. plot(cos(x), line_color="r")
  649. plot_parametric(cos(x), sin(x), line_color=lambda a: a)
  650. plot_parametric(cos(x), sin(x), line_color=1)
  651. plot_parametric(cos(x), sin(x), line_color="r")
  652. plot3d_parametric_line(cos(x), sin(x), x, line_color=lambda a: a)
  653. plot3d_parametric_line(cos(x), sin(x), x, line_color=1)
  654. plot3d_parametric_line(cos(x), sin(x), x, line_color="r")
  655. plot3d_parametric_surface(cos(x + y), sin(x - y), x - y,
  656. (x, -5, 5), (y, -5, 5),
  657. surface_color=lambda a, b: a**2 + b**2)
  658. plot3d_parametric_surface(cos(x + y), sin(x - y), x - y,
  659. (x, -5, 5), (y, -5, 5),
  660. surface_color=1)
  661. plot3d_parametric_surface(cos(x + y), sin(x - y), x - y,
  662. (x, -5, 5), (y, -5, 5),
  663. surface_color="r")
  664. plot3d(x*y, (x, -5, 5), (y, -5, 5),
  665. surface_color=lambda a, b: a**2 + b**2)
  666. plot3d(x*y, (x, -5, 5), (y, -5, 5), surface_color=1)
  667. plot3d(x*y, (x, -5, 5), (y, -5, 5), surface_color="r")
  668. @pytest.mark.parametrize("adaptive", [True, False])
  669. def test_deprecated_get_segments(adaptive):
  670. if not matplotlib:
  671. skip("Matplotlib not the default backend")
  672. x = Symbol('x')
  673. f = sin(x)
  674. p = plot(f, (x, -10, 10), show=False, adaptive=adaptive, n=10)
  675. with warns_deprecated_sympy():
  676. p[0].get_segments()
  677. @pytest.mark.parametrize("adaptive", [True, False])
  678. def test_generic_data_series(adaptive):
  679. # verify that no errors are raised when generic data series are used
  680. if not matplotlib:
  681. skip("Matplotlib not the default backend")
  682. x = Symbol("x")
  683. p = plot(x,
  684. markers=[{"args":[[0, 1], [0, 1]], "marker": "*", "linestyle": "none"}],
  685. annotations=[{"text": "test", "xy": (0, 0)}],
  686. fill={"x": [0, 1, 2, 3], "y1": [0, 1, 2, 3]},
  687. rectangles=[{"xy": (0, 0), "width": 5, "height": 1}],
  688. adaptive=adaptive, n=10)
  689. assert len(p._backend.ax.collections) == 1
  690. assert len(p._backend.ax.patches) == 1
  691. assert len(p._backend.ax.lines) == 2
  692. assert len(p._backend.ax.texts) == 1
  693. def test_deprecated_markers_annotations_rectangles_fill():
  694. if not matplotlib:
  695. skip("Matplotlib not the default backend")
  696. x = Symbol('x')
  697. p = plot(sin(x), (x, -10, 10), show=False)
  698. with warns_deprecated_sympy():
  699. p.markers = [{"args":[[0, 1], [0, 1]], "marker": "*", "linestyle": "none"}]
  700. assert len(p._series) == 2
  701. with warns_deprecated_sympy():
  702. p.annotations = [{"text": "test", "xy": (0, 0)}]
  703. assert len(p._series) == 3
  704. with warns_deprecated_sympy():
  705. p.fill = {"x": [0, 1, 2, 3], "y1": [0, 1, 2, 3]}
  706. assert len(p._series) == 4
  707. with warns_deprecated_sympy():
  708. p.rectangles = [{"xy": (0, 0), "width": 5, "height": 1}]
  709. assert len(p._series) == 5
  710. def test_back_compatibility():
  711. if not matplotlib:
  712. skip("Matplotlib not the default backend")
  713. x = Symbol('x')
  714. y = Symbol('y')
  715. p = plot(sin(x), adaptive=False, n=5)
  716. assert len(p[0].get_points()) == 2
  717. assert len(p[0].get_data()) == 2
  718. p = plot_parametric(cos(x), sin(x), (x, 0, 2), adaptive=False, n=5)
  719. assert len(p[0].get_points()) == 2
  720. assert len(p[0].get_data()) == 3
  721. p = plot3d_parametric_line(cos(x), sin(x), x, (x, 0, 2),
  722. adaptive=False, n=5)
  723. assert len(p[0].get_points()) == 3
  724. assert len(p[0].get_data()) == 4
  725. p = plot3d(cos(x**2 + y**2), (x, -pi, pi), (y, -pi, pi), n=5)
  726. assert len(p[0].get_meshes()) == 3
  727. assert len(p[0].get_data()) == 3
  728. p = plot_contour(cos(x**2 + y**2), (x, -pi, pi), (y, -pi, pi), n=5)
  729. assert len(p[0].get_meshes()) == 3
  730. assert len(p[0].get_data()) == 3
  731. p = plot3d_parametric_surface(x * cos(y), x * sin(y), x * cos(4 * y) / 2,
  732. (x, 0, pi), (y, 0, 2*pi), n=5)
  733. assert len(p[0].get_meshes()) == 3
  734. assert len(p[0].get_data()) == 5
  735. def test_plot_arguments():
  736. ### Test arguments for plot()
  737. if not matplotlib:
  738. skip("Matplotlib not the default backend")
  739. x, y = symbols("x, y")
  740. # single expressions
  741. p = plot(x + 1)
  742. assert isinstance(p[0], LineOver1DRangeSeries)
  743. assert p[0].expr == x + 1
  744. assert p[0].ranges == [(x, -10, 10)]
  745. assert p[0].get_label(False) == "x + 1"
  746. assert p[0].rendering_kw == {}
  747. # single expressions custom label
  748. p = plot(x + 1, "label")
  749. assert isinstance(p[0], LineOver1DRangeSeries)
  750. assert p[0].expr == x + 1
  751. assert p[0].ranges == [(x, -10, 10)]
  752. assert p[0].get_label(False) == "label"
  753. assert p[0].rendering_kw == {}
  754. # single expressions with range
  755. p = plot(x + 1, (x, -2, 2))
  756. assert p[0].ranges == [(x, -2, 2)]
  757. # single expressions with range, label and rendering-kw dictionary
  758. p = plot(x + 1, (x, -2, 2), "test", {"color": "r"})
  759. assert p[0].get_label(False) == "test"
  760. assert p[0].rendering_kw == {"color": "r"}
  761. # multiple expressions
  762. p = plot(x + 1, x**2)
  763. assert isinstance(p[0], LineOver1DRangeSeries)
  764. assert p[0].expr == x + 1
  765. assert p[0].ranges == [(x, -10, 10)]
  766. assert p[0].get_label(False) == "x + 1"
  767. assert p[0].rendering_kw == {}
  768. assert isinstance(p[1], LineOver1DRangeSeries)
  769. assert p[1].expr == x**2
  770. assert p[1].ranges == [(x, -10, 10)]
  771. assert p[1].get_label(False) == "x**2"
  772. assert p[1].rendering_kw == {}
  773. # multiple expressions over the same range
  774. p = plot(x + 1, x**2, (x, 0, 5))
  775. assert p[0].ranges == [(x, 0, 5)]
  776. assert p[1].ranges == [(x, 0, 5)]
  777. # multiple expressions over the same range with the same rendering kws
  778. p = plot(x + 1, x**2, (x, 0, 5), {"color": "r"})
  779. assert p[0].ranges == [(x, 0, 5)]
  780. assert p[1].ranges == [(x, 0, 5)]
  781. assert p[0].rendering_kw == {"color": "r"}
  782. assert p[1].rendering_kw == {"color": "r"}
  783. # multiple expressions with different ranges, labels and rendering kws
  784. p = plot(
  785. (x + 1, (x, 0, 5)),
  786. (x**2, (x, -2, 2), "test", {"color": "r"}))
  787. assert isinstance(p[0], LineOver1DRangeSeries)
  788. assert p[0].expr == x + 1
  789. assert p[0].ranges == [(x, 0, 5)]
  790. assert p[0].get_label(False) == "x + 1"
  791. assert p[0].rendering_kw == {}
  792. assert isinstance(p[1], LineOver1DRangeSeries)
  793. assert p[1].expr == x**2
  794. assert p[1].ranges == [(x, -2, 2)]
  795. assert p[1].get_label(False) == "test"
  796. assert p[1].rendering_kw == {"color": "r"}
  797. # single argument: lambda function
  798. f = lambda t: t
  799. p = plot(lambda t: t)
  800. assert isinstance(p[0], LineOver1DRangeSeries)
  801. assert callable(p[0].expr)
  802. assert p[0].ranges[0][1:] == (-10, 10)
  803. assert p[0].get_label(False) == ""
  804. assert p[0].rendering_kw == {}
  805. # single argument: lambda function + custom range and label
  806. p = plot(f, ("t", -5, 6), "test")
  807. assert p[0].ranges[0][1:] == (-5, 6)
  808. assert p[0].get_label(False) == "test"
  809. def test_plot_parametric_arguments():
  810. ### Test arguments for plot_parametric()
  811. if not matplotlib:
  812. skip("Matplotlib not the default backend")
  813. x, y = symbols("x, y")
  814. # single parametric expression
  815. p = plot_parametric(x + 1, x)
  816. assert isinstance(p[0], Parametric2DLineSeries)
  817. assert p[0].expr == (x + 1, x)
  818. assert p[0].ranges == [(x, -10, 10)]
  819. assert p[0].get_label(False) == "x"
  820. assert p[0].rendering_kw == {}
  821. # single parametric expression with custom range, label and rendering kws
  822. p = plot_parametric(x + 1, x, (x, -2, 2), "test",
  823. {"cmap": "Reds"})
  824. assert p[0].expr == (x + 1, x)
  825. assert p[0].ranges == [(x, -2, 2)]
  826. assert p[0].get_label(False) == "test"
  827. assert p[0].rendering_kw == {"cmap": "Reds"}
  828. p = plot_parametric((x + 1, x), (x, -2, 2), "test")
  829. assert p[0].expr == (x + 1, x)
  830. assert p[0].ranges == [(x, -2, 2)]
  831. assert p[0].get_label(False) == "test"
  832. assert p[0].rendering_kw == {}
  833. # multiple parametric expressions same symbol
  834. p = plot_parametric((x + 1, x), (x ** 2, x + 1))
  835. assert p[0].expr == (x + 1, x)
  836. assert p[0].ranges == [(x, -10, 10)]
  837. assert p[0].get_label(False) == "x"
  838. assert p[0].rendering_kw == {}
  839. assert p[1].expr == (x ** 2, x + 1)
  840. assert p[1].ranges == [(x, -10, 10)]
  841. assert p[1].get_label(False) == "x"
  842. assert p[1].rendering_kw == {}
  843. # multiple parametric expressions different symbols
  844. p = plot_parametric((x + 1, x), (y ** 2, y + 1, "test"))
  845. assert p[0].expr == (x + 1, x)
  846. assert p[0].ranges == [(x, -10, 10)]
  847. assert p[0].get_label(False) == "x"
  848. assert p[0].rendering_kw == {}
  849. assert p[1].expr == (y ** 2, y + 1)
  850. assert p[1].ranges == [(y, -10, 10)]
  851. assert p[1].get_label(False) == "test"
  852. assert p[1].rendering_kw == {}
  853. # multiple parametric expressions same range
  854. p = plot_parametric((x + 1, x), (x ** 2, x + 1), (x, -2, 2))
  855. assert p[0].expr == (x + 1, x)
  856. assert p[0].ranges == [(x, -2, 2)]
  857. assert p[0].get_label(False) == "x"
  858. assert p[0].rendering_kw == {}
  859. assert p[1].expr == (x ** 2, x + 1)
  860. assert p[1].ranges == [(x, -2, 2)]
  861. assert p[1].get_label(False) == "x"
  862. assert p[1].rendering_kw == {}
  863. # multiple parametric expressions, custom ranges and labels
  864. p = plot_parametric(
  865. (x + 1, x, (x, -2, 2), "test1"),
  866. (x ** 2, x + 1, (x, -3, 3), "test2", {"cmap": "Reds"}))
  867. assert p[0].expr == (x + 1, x)
  868. assert p[0].ranges == [(x, -2, 2)]
  869. assert p[0].get_label(False) == "test1"
  870. assert p[0].rendering_kw == {}
  871. assert p[1].expr == (x ** 2, x + 1)
  872. assert p[1].ranges == [(x, -3, 3)]
  873. assert p[1].get_label(False) == "test2"
  874. assert p[1].rendering_kw == {"cmap": "Reds"}
  875. # single argument: lambda function
  876. fx = lambda t: t
  877. fy = lambda t: 2 * t
  878. p = plot_parametric(fx, fy)
  879. assert all(callable(t) for t in p[0].expr)
  880. assert p[0].ranges[0][1:] == (-10, 10)
  881. assert "Dummy" in p[0].get_label(False)
  882. assert p[0].rendering_kw == {}
  883. # single argument: lambda function + custom range + label
  884. p = plot_parametric(fx, fy, ("t", 0, 2), "test")
  885. assert all(callable(t) for t in p[0].expr)
  886. assert p[0].ranges[0][1:] == (0, 2)
  887. assert p[0].get_label(False) == "test"
  888. assert p[0].rendering_kw == {}
  889. def test_plot3d_parametric_line_arguments():
  890. ### Test arguments for plot3d_parametric_line()
  891. if not matplotlib:
  892. skip("Matplotlib not the default backend")
  893. x, y = symbols("x, y")
  894. # single parametric expression
  895. p = plot3d_parametric_line(x + 1, x, sin(x))
  896. assert isinstance(p[0], Parametric3DLineSeries)
  897. assert p[0].expr == (x + 1, x, sin(x))
  898. assert p[0].ranges == [(x, -10, 10)]
  899. assert p[0].get_label(False) == "x"
  900. assert p[0].rendering_kw == {}
  901. # single parametric expression with custom range, label and rendering kws
  902. p = plot3d_parametric_line(x + 1, x, sin(x), (x, -2, 2),
  903. "test", {"cmap": "Reds"})
  904. assert isinstance(p[0], Parametric3DLineSeries)
  905. assert p[0].expr == (x + 1, x, sin(x))
  906. assert p[0].ranges == [(x, -2, 2)]
  907. assert p[0].get_label(False) == "test"
  908. assert p[0].rendering_kw == {"cmap": "Reds"}
  909. p = plot3d_parametric_line((x + 1, x, sin(x)), (x, -2, 2), "test")
  910. assert p[0].expr == (x + 1, x, sin(x))
  911. assert p[0].ranges == [(x, -2, 2)]
  912. assert p[0].get_label(False) == "test"
  913. assert p[0].rendering_kw == {}
  914. # multiple parametric expression same symbol
  915. p = plot3d_parametric_line(
  916. (x + 1, x, sin(x)), (x ** 2, 1, cos(x), {"cmap": "Reds"}))
  917. assert p[0].expr == (x + 1, x, sin(x))
  918. assert p[0].ranges == [(x, -10, 10)]
  919. assert p[0].get_label(False) == "x"
  920. assert p[0].rendering_kw == {}
  921. assert p[1].expr == (x ** 2, 1, cos(x))
  922. assert p[1].ranges == [(x, -10, 10)]
  923. assert p[1].get_label(False) == "x"
  924. assert p[1].rendering_kw == {"cmap": "Reds"}
  925. # multiple parametric expression different symbols
  926. p = plot3d_parametric_line((x + 1, x, sin(x)), (y ** 2, 1, cos(y)))
  927. assert p[0].expr == (x + 1, x, sin(x))
  928. assert p[0].ranges == [(x, -10, 10)]
  929. assert p[0].get_label(False) == "x"
  930. assert p[0].rendering_kw == {}
  931. assert p[1].expr == (y ** 2, 1, cos(y))
  932. assert p[1].ranges == [(y, -10, 10)]
  933. assert p[1].get_label(False) == "y"
  934. assert p[1].rendering_kw == {}
  935. # multiple parametric expression, custom ranges and labels
  936. p = plot3d_parametric_line(
  937. (x + 1, x, sin(x)),
  938. (x ** 2, 1, cos(x), (x, -2, 2), "test", {"cmap": "Reds"}))
  939. assert p[0].expr == (x + 1, x, sin(x))
  940. assert p[0].ranges == [(x, -10, 10)]
  941. assert p[0].get_label(False) == "x"
  942. assert p[0].rendering_kw == {}
  943. assert p[1].expr == (x ** 2, 1, cos(x))
  944. assert p[1].ranges == [(x, -2, 2)]
  945. assert p[1].get_label(False) == "test"
  946. assert p[1].rendering_kw == {"cmap": "Reds"}
  947. # single argument: lambda function
  948. fx = lambda t: t
  949. fy = lambda t: 2 * t
  950. fz = lambda t: 3 * t
  951. p = plot3d_parametric_line(fx, fy, fz)
  952. assert all(callable(t) for t in p[0].expr)
  953. assert p[0].ranges[0][1:] == (-10, 10)
  954. assert "Dummy" in p[0].get_label(False)
  955. assert p[0].rendering_kw == {}
  956. # single argument: lambda function + custom range + label
  957. p = plot3d_parametric_line(fx, fy, fz, ("t", 0, 2), "test")
  958. assert all(callable(t) for t in p[0].expr)
  959. assert p[0].ranges[0][1:] == (0, 2)
  960. assert p[0].get_label(False) == "test"
  961. assert p[0].rendering_kw == {}
  962. def test_plot3d_plot_contour_arguments():
  963. ### Test arguments for plot3d() and plot_contour()
  964. if not matplotlib:
  965. skip("Matplotlib not the default backend")
  966. x, y = symbols("x, y")
  967. # single expression
  968. p = plot3d(x + y)
  969. assert isinstance(p[0], SurfaceOver2DRangeSeries)
  970. assert p[0].expr == x + y
  971. assert p[0].ranges[0] == (x, -10, 10) or (y, -10, 10)
  972. assert p[0].ranges[1] == (x, -10, 10) or (y, -10, 10)
  973. assert p[0].get_label(False) == "x + y"
  974. assert p[0].rendering_kw == {}
  975. # single expression, custom range, label and rendering kws
  976. p = plot3d(x + y, (x, -2, 2), "test", {"cmap": "Reds"})
  977. assert isinstance(p[0], SurfaceOver2DRangeSeries)
  978. assert p[0].expr == x + y
  979. assert p[0].ranges[0] == (x, -2, 2)
  980. assert p[0].ranges[1] == (y, -10, 10)
  981. assert p[0].get_label(False) == "test"
  982. assert p[0].rendering_kw == {"cmap": "Reds"}
  983. p = plot3d(x + y, (x, -2, 2), (y, -4, 4), "test")
  984. assert p[0].ranges[0] == (x, -2, 2)
  985. assert p[0].ranges[1] == (y, -4, 4)
  986. # multiple expressions
  987. p = plot3d(x + y, x * y)
  988. assert p[0].expr == x + y
  989. assert p[0].ranges[0] == (x, -10, 10) or (y, -10, 10)
  990. assert p[0].ranges[1] == (x, -10, 10) or (y, -10, 10)
  991. assert p[0].get_label(False) == "x + y"
  992. assert p[0].rendering_kw == {}
  993. assert p[1].expr == x * y
  994. assert p[1].ranges[0] == (x, -10, 10) or (y, -10, 10)
  995. assert p[1].ranges[1] == (x, -10, 10) or (y, -10, 10)
  996. assert p[1].get_label(False) == "x*y"
  997. assert p[1].rendering_kw == {}
  998. # multiple expressions, same custom ranges
  999. p = plot3d(x + y, x * y, (x, -2, 2), (y, -4, 4))
  1000. assert p[0].expr == x + y
  1001. assert p[0].ranges[0] == (x, -2, 2)
  1002. assert p[0].ranges[1] == (y, -4, 4)
  1003. assert p[0].get_label(False) == "x + y"
  1004. assert p[0].rendering_kw == {}
  1005. assert p[1].expr == x * y
  1006. assert p[1].ranges[0] == (x, -2, 2)
  1007. assert p[1].ranges[1] == (y, -4, 4)
  1008. assert p[1].get_label(False) == "x*y"
  1009. assert p[1].rendering_kw == {}
  1010. # multiple expressions, custom ranges, labels and rendering kws
  1011. p = plot3d(
  1012. (x + y, (x, -2, 2), (y, -4, 4)),
  1013. (x * y, (x, -3, 3), (y, -6, 6), "test", {"cmap": "Reds"}))
  1014. assert p[0].expr == x + y
  1015. assert p[0].ranges[0] == (x, -2, 2)
  1016. assert p[0].ranges[1] == (y, -4, 4)
  1017. assert p[0].get_label(False) == "x + y"
  1018. assert p[0].rendering_kw == {}
  1019. assert p[1].expr == x * y
  1020. assert p[1].ranges[0] == (x, -3, 3)
  1021. assert p[1].ranges[1] == (y, -6, 6)
  1022. assert p[1].get_label(False) == "test"
  1023. assert p[1].rendering_kw == {"cmap": "Reds"}
  1024. # single expression: lambda function
  1025. f = lambda x, y: x + y
  1026. p = plot3d(f)
  1027. assert callable(p[0].expr)
  1028. assert p[0].ranges[0][1:] == (-10, 10)
  1029. assert p[0].ranges[1][1:] == (-10, 10)
  1030. assert p[0].get_label(False) == ""
  1031. assert p[0].rendering_kw == {}
  1032. # single expression: lambda function + custom ranges + label
  1033. p = plot3d(f, ("a", -5, 3), ("b", -2, 1), "test")
  1034. assert callable(p[0].expr)
  1035. assert p[0].ranges[0][1:] == (-5, 3)
  1036. assert p[0].ranges[1][1:] == (-2, 1)
  1037. assert p[0].get_label(False) == "test"
  1038. assert p[0].rendering_kw == {}
  1039. # test issue 25818
  1040. # single expression, custom range, min/max functions
  1041. p = plot3d(Min(x, y), (x, 0, 10), (y, 0, 10))
  1042. assert isinstance(p[0], SurfaceOver2DRangeSeries)
  1043. assert p[0].expr == Min(x, y)
  1044. assert p[0].ranges[0] == (x, 0, 10)
  1045. assert p[0].ranges[1] == (y, 0, 10)
  1046. assert p[0].get_label(False) == "Min(x, y)"
  1047. assert p[0].rendering_kw == {}
  1048. def test_plot3d_parametric_surface_arguments():
  1049. ### Test arguments for plot3d_parametric_surface()
  1050. if not matplotlib:
  1051. skip("Matplotlib not the default backend")
  1052. x, y = symbols("x, y")
  1053. # single parametric expression
  1054. p = plot3d_parametric_surface(x + y, cos(x + y), sin(x + y))
  1055. assert isinstance(p[0], ParametricSurfaceSeries)
  1056. assert p[0].expr == (x + y, cos(x + y), sin(x + y))
  1057. assert p[0].ranges[0] == (x, -10, 10) or (y, -10, 10)
  1058. assert p[0].ranges[1] == (x, -10, 10) or (y, -10, 10)
  1059. assert p[0].get_label(False) == "(x + y, cos(x + y), sin(x + y))"
  1060. assert p[0].rendering_kw == {}
  1061. # single parametric expression, custom ranges, labels and rendering kws
  1062. p = plot3d_parametric_surface(x + y, cos(x + y), sin(x + y),
  1063. (x, -2, 2), (y, -4, 4), "test", {"cmap": "Reds"})
  1064. assert isinstance(p[0], ParametricSurfaceSeries)
  1065. assert p[0].expr == (x + y, cos(x + y), sin(x + y))
  1066. assert p[0].ranges[0] == (x, -2, 2)
  1067. assert p[0].ranges[1] == (y, -4, 4)
  1068. assert p[0].get_label(False) == "test"
  1069. assert p[0].rendering_kw == {"cmap": "Reds"}
  1070. # multiple parametric expressions
  1071. p = plot3d_parametric_surface(
  1072. (x + y, cos(x + y), sin(x + y)),
  1073. (x - y, cos(x - y), sin(x - y), "test"))
  1074. assert p[0].expr == (x + y, cos(x + y), sin(x + y))
  1075. assert p[0].ranges[0] == (x, -10, 10) or (y, -10, 10)
  1076. assert p[0].ranges[1] == (x, -10, 10) or (y, -10, 10)
  1077. assert p[0].get_label(False) == "(x + y, cos(x + y), sin(x + y))"
  1078. assert p[0].rendering_kw == {}
  1079. assert p[1].expr == (x - y, cos(x - y), sin(x - y))
  1080. assert p[1].ranges[0] == (x, -10, 10) or (y, -10, 10)
  1081. assert p[1].ranges[1] == (x, -10, 10) or (y, -10, 10)
  1082. assert p[1].get_label(False) == "test"
  1083. assert p[1].rendering_kw == {}
  1084. # multiple parametric expressions, custom ranges and labels
  1085. p = plot3d_parametric_surface(
  1086. (x + y, cos(x + y), sin(x + y), (x, -2, 2), "test"),
  1087. (x - y, cos(x - y), sin(x - y), (x, -3, 3), (y, -4, 4),
  1088. "test2", {"cmap": "Reds"}))
  1089. assert p[0].expr == (x + y, cos(x + y), sin(x + y))
  1090. assert p[0].ranges[0] == (x, -2, 2)
  1091. assert p[0].ranges[1] == (y, -10, 10)
  1092. assert p[0].get_label(False) == "test"
  1093. assert p[0].rendering_kw == {}
  1094. assert p[1].expr == (x - y, cos(x - y), sin(x - y))
  1095. assert p[1].ranges[0] == (x, -3, 3)
  1096. assert p[1].ranges[1] == (y, -4, 4)
  1097. assert p[1].get_label(False) == "test2"
  1098. assert p[1].rendering_kw == {"cmap": "Reds"}
  1099. # lambda functions instead of symbolic expressions for a single 3D
  1100. # parametric surface
  1101. p = plot3d_parametric_surface(
  1102. lambda u, v: u, lambda u, v: v, lambda u, v: u + v,
  1103. ("u", 0, 2), ("v", -3, 4))
  1104. assert all(callable(t) for t in p[0].expr)
  1105. assert p[0].ranges[0][1:] == (-0, 2)
  1106. assert p[0].ranges[1][1:] == (-3, 4)
  1107. assert p[0].get_label(False) == ""
  1108. assert p[0].rendering_kw == {}
  1109. # lambda functions instead of symbolic expressions for multiple 3D
  1110. # parametric surfaces
  1111. p = plot3d_parametric_surface(
  1112. (lambda u, v: u, lambda u, v: v, lambda u, v: u + v,
  1113. ("u", 0, 2), ("v", -3, 4)),
  1114. (lambda u, v: v, lambda u, v: u, lambda u, v: u - v,
  1115. ("u", -2, 3), ("v", -4, 5), "test"))
  1116. assert all(callable(t) for t in p[0].expr)
  1117. assert p[0].ranges[0][1:] == (0, 2)
  1118. assert p[0].ranges[1][1:] == (-3, 4)
  1119. assert p[0].get_label(False) == ""
  1120. assert p[0].rendering_kw == {}
  1121. assert all(callable(t) for t in p[1].expr)
  1122. assert p[1].ranges[0][1:] == (-2, 3)
  1123. assert p[1].ranges[1][1:] == (-4, 5)
  1124. assert p[1].get_label(False) == "test"
  1125. assert p[1].rendering_kw == {}