intpoly.py 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302
  1. """
  2. Module to implement integration of uni/bivariate polynomials over
  3. 2D Polytopes and uni/bi/trivariate polynomials over 3D Polytopes.
  4. Uses evaluation techniques as described in Chin et al. (2015) [1].
  5. References
  6. ===========
  7. .. [1] Chin, Eric B., Jean B. Lasserre, and N. Sukumar. "Numerical integration
  8. of homogeneous functions on convex and nonconvex polygons and polyhedra."
  9. Computational Mechanics 56.6 (2015): 967-981
  10. PDF link : http://dilbert.engr.ucdavis.edu/~suku/quadrature/cls-integration.pdf
  11. """
  12. from functools import cmp_to_key
  13. from sympy.abc import x, y, z
  14. from sympy.core import S, diff, Expr, Symbol
  15. from sympy.core.sympify import _sympify
  16. from sympy.geometry import Segment2D, Polygon, Point, Point2D
  17. from sympy.polys.polytools import LC, gcd_list, degree_list, Poly
  18. from sympy.simplify.simplify import nsimplify
  19. def polytope_integrate(poly, expr=None, *, clockwise=False, max_degree=None):
  20. """Integrates polynomials over 2/3-Polytopes.
  21. Explanation
  22. ===========
  23. This function accepts the polytope in ``poly`` and the function in ``expr``
  24. (uni/bi/trivariate polynomials are implemented) and returns
  25. the exact integral of ``expr`` over ``poly``.
  26. Parameters
  27. ==========
  28. poly : The input Polygon.
  29. expr : The input polynomial.
  30. clockwise : Binary value to sort input points of 2-Polytope clockwise.(Optional)
  31. max_degree : The maximum degree of any monomial of the input polynomial.(Optional)
  32. Examples
  33. ========
  34. >>> from sympy.abc import x, y
  35. >>> from sympy import Point, Polygon
  36. >>> from sympy.integrals.intpoly import polytope_integrate
  37. >>> polygon = Polygon(Point(0, 0), Point(0, 1), Point(1, 1), Point(1, 0))
  38. >>> polys = [1, x, y, x*y, x**2*y, x*y**2]
  39. >>> expr = x*y
  40. >>> polytope_integrate(polygon, expr)
  41. 1/4
  42. >>> polytope_integrate(polygon, polys, max_degree=3)
  43. {1: 1, x: 1/2, y: 1/2, x*y: 1/4, x*y**2: 1/6, x**2*y: 1/6}
  44. """
  45. if clockwise:
  46. if isinstance(poly, Polygon):
  47. poly = Polygon(*point_sort(poly.vertices), evaluate=False)
  48. else:
  49. raise TypeError("clockwise=True works for only 2-Polytope"
  50. "V-representation input")
  51. if isinstance(poly, Polygon):
  52. # For Vertex Representation(2D case)
  53. hp_params = hyperplane_parameters(poly)
  54. facets = poly.sides
  55. elif len(poly[0]) == 2:
  56. # For Hyperplane Representation(2D case)
  57. plen = len(poly)
  58. if len(poly[0][0]) == 2:
  59. intersections = [intersection(poly[(i - 1) % plen], poly[i],
  60. "plane2D")
  61. for i in range(0, plen)]
  62. hp_params = poly
  63. lints = len(intersections)
  64. facets = [Segment2D(intersections[i],
  65. intersections[(i + 1) % lints])
  66. for i in range(lints)]
  67. else:
  68. raise NotImplementedError("Integration for H-representation 3D"
  69. "case not implemented yet.")
  70. else:
  71. # For Vertex Representation(3D case)
  72. vertices = poly[0]
  73. facets = poly[1:]
  74. hp_params = hyperplane_parameters(facets, vertices)
  75. if max_degree is None:
  76. if expr is None:
  77. raise TypeError('Input expression must be a valid SymPy expression')
  78. return main_integrate3d(expr, facets, vertices, hp_params)
  79. if max_degree is not None:
  80. result = {}
  81. if expr is not None:
  82. f_expr = []
  83. for e in expr:
  84. _ = decompose(e)
  85. if len(_) == 1 and not _.popitem()[0]:
  86. f_expr.append(e)
  87. elif Poly(e).total_degree() <= max_degree:
  88. f_expr.append(e)
  89. expr = f_expr
  90. if not isinstance(expr, list) and expr is not None:
  91. raise TypeError('Input polynomials must be list of expressions')
  92. if len(hp_params[0][0]) == 3:
  93. result_dict = main_integrate3d(0, facets, vertices, hp_params,
  94. max_degree)
  95. else:
  96. result_dict = main_integrate(0, facets, hp_params, max_degree)
  97. if expr is None:
  98. return result_dict
  99. for poly in expr:
  100. poly = _sympify(poly)
  101. if poly not in result:
  102. if poly.is_zero:
  103. result[S.Zero] = S.Zero
  104. continue
  105. integral_value = S.Zero
  106. monoms = decompose(poly, separate=True)
  107. for monom in monoms:
  108. monom = nsimplify(monom)
  109. coeff, m = strip(monom)
  110. integral_value += result_dict[m] * coeff
  111. result[poly] = integral_value
  112. return result
  113. if expr is None:
  114. raise TypeError('Input expression must be a valid SymPy expression')
  115. return main_integrate(expr, facets, hp_params)
  116. def strip(monom):
  117. if monom.is_zero:
  118. return S.Zero, S.Zero
  119. elif monom.is_number:
  120. return monom, S.One
  121. else:
  122. coeff = LC(monom)
  123. return coeff, monom / coeff
  124. def _polynomial_integrate(polynomials, facets, hp_params):
  125. dims = (x, y)
  126. dim_length = len(dims)
  127. integral_value = S.Zero
  128. for deg in polynomials:
  129. poly_contribute = S.Zero
  130. facet_count = 0
  131. for hp in hp_params:
  132. value_over_boundary = integration_reduction(facets,
  133. facet_count,
  134. hp[0], hp[1],
  135. polynomials[deg],
  136. dims, deg)
  137. poly_contribute += value_over_boundary * (hp[1] / norm(hp[0]))
  138. facet_count += 1
  139. poly_contribute /= (dim_length + deg)
  140. integral_value += poly_contribute
  141. return integral_value
  142. def main_integrate3d(expr, facets, vertices, hp_params, max_degree=None):
  143. """Function to translate the problem of integrating uni/bi/tri-variate
  144. polynomials over a 3-Polytope to integrating over its faces.
  145. This is done using Generalized Stokes' Theorem and Euler's Theorem.
  146. Parameters
  147. ==========
  148. expr :
  149. The input polynomial.
  150. facets :
  151. Faces of the 3-Polytope(expressed as indices of `vertices`).
  152. vertices :
  153. Vertices that constitute the Polytope.
  154. hp_params :
  155. Hyperplane Parameters of the facets.
  156. max_degree : optional
  157. Max degree of constituent monomial in given list of polynomial.
  158. Examples
  159. ========
  160. >>> from sympy.integrals.intpoly import main_integrate3d, \
  161. hyperplane_parameters
  162. >>> cube = [[(0, 0, 0), (0, 0, 5), (0, 5, 0), (0, 5, 5), (5, 0, 0),\
  163. (5, 0, 5), (5, 5, 0), (5, 5, 5)],\
  164. [2, 6, 7, 3], [3, 7, 5, 1], [7, 6, 4, 5], [1, 5, 4, 0],\
  165. [3, 1, 0, 2], [0, 4, 6, 2]]
  166. >>> vertices = cube[0]
  167. >>> faces = cube[1:]
  168. >>> hp_params = hyperplane_parameters(faces, vertices)
  169. >>> main_integrate3d(1, faces, vertices, hp_params)
  170. -125
  171. """
  172. result = {}
  173. dims = (x, y, z)
  174. dim_length = len(dims)
  175. if max_degree:
  176. grad_terms = gradient_terms(max_degree, 3)
  177. flat_list = [term for z_terms in grad_terms
  178. for x_term in z_terms
  179. for term in x_term]
  180. for term in flat_list:
  181. result[term[0]] = 0
  182. for facet_count, hp in enumerate(hp_params):
  183. a, b = hp[0], hp[1]
  184. x0 = vertices[facets[facet_count][0]]
  185. for i, monom in enumerate(flat_list):
  186. # Every monomial is a tuple :
  187. # (term, x_degree, y_degree, z_degree, value over boundary)
  188. expr, x_d, y_d, z_d, z_index, y_index, x_index, _ = monom
  189. degree = x_d + y_d + z_d
  190. if b.is_zero:
  191. value_over_face = S.Zero
  192. else:
  193. value_over_face = \
  194. integration_reduction_dynamic(facets, facet_count, a,
  195. b, expr, degree, dims,
  196. x_index, y_index,
  197. z_index, x0, grad_terms,
  198. i, vertices, hp)
  199. monom[7] = value_over_face
  200. result[expr] += value_over_face * \
  201. (b / norm(a)) / (dim_length + x_d + y_d + z_d)
  202. return result
  203. else:
  204. integral_value = S.Zero
  205. polynomials = decompose(expr)
  206. for deg in polynomials:
  207. poly_contribute = S.Zero
  208. facet_count = 0
  209. for i, facet in enumerate(facets):
  210. hp = hp_params[i]
  211. if hp[1].is_zero:
  212. continue
  213. pi = polygon_integrate(facet, hp, i, facets, vertices, expr, deg)
  214. poly_contribute += pi *\
  215. (hp[1] / norm(tuple(hp[0])))
  216. facet_count += 1
  217. poly_contribute /= (dim_length + deg)
  218. integral_value += poly_contribute
  219. return integral_value
  220. def main_integrate(expr, facets, hp_params, max_degree=None):
  221. """Function to translate the problem of integrating univariate/bivariate
  222. polynomials over a 2-Polytope to integrating over its boundary facets.
  223. This is done using Generalized Stokes's Theorem and Euler's Theorem.
  224. Parameters
  225. ==========
  226. expr :
  227. The input polynomial.
  228. facets :
  229. Facets(Line Segments) of the 2-Polytope.
  230. hp_params :
  231. Hyperplane Parameters of the facets.
  232. max_degree : optional
  233. The maximum degree of any monomial of the input polynomial.
  234. >>> from sympy.abc import x, y
  235. >>> from sympy.integrals.intpoly import main_integrate,\
  236. hyperplane_parameters
  237. >>> from sympy import Point, Polygon
  238. >>> triangle = Polygon(Point(0, 3), Point(5, 3), Point(1, 1))
  239. >>> facets = triangle.sides
  240. >>> hp_params = hyperplane_parameters(triangle)
  241. >>> main_integrate(x**2 + y**2, facets, hp_params)
  242. 325/6
  243. """
  244. dims = (x, y)
  245. dim_length = len(dims)
  246. result = {}
  247. if max_degree:
  248. grad_terms = [[0, 0, 0, 0]] + gradient_terms(max_degree)
  249. for facet_count, hp in enumerate(hp_params):
  250. a, b = hp[0], hp[1]
  251. x0 = facets[facet_count].points[0]
  252. for i, monom in enumerate(grad_terms):
  253. # Every monomial is a tuple :
  254. # (term, x_degree, y_degree, value over boundary)
  255. m, x_d, y_d, _ = monom
  256. value = result.get(m, None)
  257. degree = S.Zero
  258. if b.is_zero:
  259. value_over_boundary = S.Zero
  260. else:
  261. degree = x_d + y_d
  262. value_over_boundary = \
  263. integration_reduction_dynamic(facets, facet_count, a,
  264. b, m, degree, dims, x_d,
  265. y_d, max_degree, x0,
  266. grad_terms, i)
  267. monom[3] = value_over_boundary
  268. if value is not None:
  269. result[m] += value_over_boundary * \
  270. (b / norm(a)) / (dim_length + degree)
  271. else:
  272. result[m] = value_over_boundary * \
  273. (b / norm(a)) / (dim_length + degree)
  274. return result
  275. else:
  276. if not isinstance(expr, list):
  277. polynomials = decompose(expr)
  278. return _polynomial_integrate(polynomials, facets, hp_params)
  279. else:
  280. return {e: _polynomial_integrate(decompose(e), facets, hp_params) for e in expr}
  281. def polygon_integrate(facet, hp_param, index, facets, vertices, expr, degree):
  282. """Helper function to integrate the input uni/bi/trivariate polynomial
  283. over a certain face of the 3-Polytope.
  284. Parameters
  285. ==========
  286. facet :
  287. Particular face of the 3-Polytope over which ``expr`` is integrated.
  288. index :
  289. The index of ``facet`` in ``facets``.
  290. facets :
  291. Faces of the 3-Polytope(expressed as indices of `vertices`).
  292. vertices :
  293. Vertices that constitute the facet.
  294. expr :
  295. The input polynomial.
  296. degree :
  297. Degree of ``expr``.
  298. Examples
  299. ========
  300. >>> from sympy.integrals.intpoly import polygon_integrate
  301. >>> cube = [[(0, 0, 0), (0, 0, 5), (0, 5, 0), (0, 5, 5), (5, 0, 0),\
  302. (5, 0, 5), (5, 5, 0), (5, 5, 5)],\
  303. [2, 6, 7, 3], [3, 7, 5, 1], [7, 6, 4, 5], [1, 5, 4, 0],\
  304. [3, 1, 0, 2], [0, 4, 6, 2]]
  305. >>> facet = cube[1]
  306. >>> facets = cube[1:]
  307. >>> vertices = cube[0]
  308. >>> polygon_integrate(facet, [(0, 1, 0), 5], 0, facets, vertices, 1, 0)
  309. -25
  310. """
  311. expr = S(expr)
  312. if expr.is_zero:
  313. return S.Zero
  314. result = S.Zero
  315. x0 = vertices[facet[0]]
  316. facet_len = len(facet)
  317. for i, fac in enumerate(facet):
  318. side = (vertices[fac], vertices[facet[(i + 1) % facet_len]])
  319. result += distance_to_side(x0, side, hp_param[0]) *\
  320. lineseg_integrate(facet, i, side, expr, degree)
  321. if not expr.is_number:
  322. expr = diff(expr, x) * x0[0] + diff(expr, y) * x0[1] +\
  323. diff(expr, z) * x0[2]
  324. result += polygon_integrate(facet, hp_param, index, facets, vertices,
  325. expr, degree - 1)
  326. result /= (degree + 2)
  327. return result
  328. def distance_to_side(point, line_seg, A):
  329. """Helper function to compute the signed distance between given 3D point
  330. and a line segment.
  331. Parameters
  332. ==========
  333. point : 3D Point
  334. line_seg : Line Segment
  335. Examples
  336. ========
  337. >>> from sympy.integrals.intpoly import distance_to_side
  338. >>> point = (0, 0, 0)
  339. >>> distance_to_side(point, [(0, 0, 1), (0, 1, 0)], (1, 0, 0))
  340. -sqrt(2)/2
  341. """
  342. x1, x2 = line_seg
  343. rev_normal = [-1 * S(i)/norm(A) for i in A]
  344. vector = [x2[i] - x1[i] for i in range(0, 3)]
  345. vector = [vector[i]/norm(vector) for i in range(0, 3)]
  346. n_side = cross_product((0, 0, 0), rev_normal, vector)
  347. vectorx0 = [line_seg[0][i] - point[i] for i in range(0, 3)]
  348. dot_product = sum([vectorx0[i] * n_side[i] for i in range(0, 3)])
  349. return dot_product
  350. def lineseg_integrate(polygon, index, line_seg, expr, degree):
  351. """Helper function to compute the line integral of ``expr`` over ``line_seg``.
  352. Parameters
  353. ===========
  354. polygon :
  355. Face of a 3-Polytope.
  356. index :
  357. Index of line_seg in polygon.
  358. line_seg :
  359. Line Segment.
  360. Examples
  361. ========
  362. >>> from sympy.integrals.intpoly import lineseg_integrate
  363. >>> polygon = [(0, 5, 0), (5, 5, 0), (5, 5, 5), (0, 5, 5)]
  364. >>> line_seg = [(0, 5, 0), (5, 5, 0)]
  365. >>> lineseg_integrate(polygon, 0, line_seg, 1, 0)
  366. 5
  367. """
  368. expr = _sympify(expr)
  369. if expr.is_zero:
  370. return S.Zero
  371. result = S.Zero
  372. x0 = line_seg[0]
  373. distance = norm(tuple([line_seg[1][i] - line_seg[0][i] for i in
  374. range(3)]))
  375. if isinstance(expr, Expr):
  376. expr_dict = {x: line_seg[1][0],
  377. y: line_seg[1][1],
  378. z: line_seg[1][2]}
  379. result += distance * expr.subs(expr_dict)
  380. else:
  381. result += distance * expr
  382. expr = diff(expr, x) * x0[0] + diff(expr, y) * x0[1] +\
  383. diff(expr, z) * x0[2]
  384. result += lineseg_integrate(polygon, index, line_seg, expr, degree - 1)
  385. result /= (degree + 1)
  386. return result
  387. def integration_reduction(facets, index, a, b, expr, dims, degree):
  388. """Helper method for main_integrate. Returns the value of the input
  389. expression evaluated over the polytope facet referenced by a given index.
  390. Parameters
  391. ===========
  392. facets :
  393. List of facets of the polytope.
  394. index :
  395. Index referencing the facet to integrate the expression over.
  396. a :
  397. Hyperplane parameter denoting direction.
  398. b :
  399. Hyperplane parameter denoting distance.
  400. expr :
  401. The expression to integrate over the facet.
  402. dims :
  403. List of symbols denoting axes.
  404. degree :
  405. Degree of the homogeneous polynomial.
  406. Examples
  407. ========
  408. >>> from sympy.abc import x, y
  409. >>> from sympy.integrals.intpoly import integration_reduction,\
  410. hyperplane_parameters
  411. >>> from sympy import Point, Polygon
  412. >>> triangle = Polygon(Point(0, 3), Point(5, 3), Point(1, 1))
  413. >>> facets = triangle.sides
  414. >>> a, b = hyperplane_parameters(triangle)[0]
  415. >>> integration_reduction(facets, 0, a, b, 1, (x, y), 0)
  416. 5
  417. """
  418. expr = _sympify(expr)
  419. if expr.is_zero:
  420. return expr
  421. value = S.Zero
  422. x0 = facets[index].points[0]
  423. m = len(facets)
  424. gens = (x, y)
  425. inner_product = diff(expr, gens[0]) * x0[0] + diff(expr, gens[1]) * x0[1]
  426. if inner_product != 0:
  427. value += integration_reduction(facets, index, a, b,
  428. inner_product, dims, degree - 1)
  429. value += left_integral2D(m, index, facets, x0, expr, gens)
  430. return value/(len(dims) + degree - 1)
  431. def left_integral2D(m, index, facets, x0, expr, gens):
  432. """Computes the left integral of Eq 10 in Chin et al.
  433. For the 2D case, the integral is just an evaluation of the polynomial
  434. at the intersection of two facets which is multiplied by the distance
  435. between the first point of facet and that intersection.
  436. Parameters
  437. ==========
  438. m :
  439. No. of hyperplanes.
  440. index :
  441. Index of facet to find intersections with.
  442. facets :
  443. List of facets(Line Segments in 2D case).
  444. x0 :
  445. First point on facet referenced by index.
  446. expr :
  447. Input polynomial
  448. gens :
  449. Generators which generate the polynomial
  450. Examples
  451. ========
  452. >>> from sympy.abc import x, y
  453. >>> from sympy.integrals.intpoly import left_integral2D
  454. >>> from sympy import Point, Polygon
  455. >>> triangle = Polygon(Point(0, 3), Point(5, 3), Point(1, 1))
  456. >>> facets = triangle.sides
  457. >>> left_integral2D(3, 0, facets, facets[0].points[0], 1, (x, y))
  458. 5
  459. """
  460. value = S.Zero
  461. for j in range(m):
  462. intersect = ()
  463. if j in ((index - 1) % m, (index + 1) % m):
  464. intersect = intersection(facets[index], facets[j], "segment2D")
  465. if intersect:
  466. distance_origin = norm(tuple(map(lambda x, y: x - y,
  467. intersect, x0)))
  468. if is_vertex(intersect):
  469. if isinstance(expr, Expr):
  470. if len(gens) == 3:
  471. expr_dict = {gens[0]: intersect[0],
  472. gens[1]: intersect[1],
  473. gens[2]: intersect[2]}
  474. else:
  475. expr_dict = {gens[0]: intersect[0],
  476. gens[1]: intersect[1]}
  477. value += distance_origin * expr.subs(expr_dict)
  478. else:
  479. value += distance_origin * expr
  480. return value
  481. def integration_reduction_dynamic(facets, index, a, b, expr, degree, dims,
  482. x_index, y_index, max_index, x0,
  483. monomial_values, monom_index, vertices=None,
  484. hp_param=None):
  485. """The same integration_reduction function which uses a dynamic
  486. programming approach to compute terms by using the values of the integral
  487. of previously computed terms.
  488. Parameters
  489. ==========
  490. facets :
  491. Facets of the Polytope.
  492. index :
  493. Index of facet to find intersections with.(Used in left_integral()).
  494. a, b :
  495. Hyperplane parameters.
  496. expr :
  497. Input monomial.
  498. degree :
  499. Total degree of ``expr``.
  500. dims :
  501. Tuple denoting axes variables.
  502. x_index :
  503. Exponent of 'x' in ``expr``.
  504. y_index :
  505. Exponent of 'y' in ``expr``.
  506. max_index :
  507. Maximum exponent of any monomial in ``monomial_values``.
  508. x0 :
  509. First point on ``facets[index]``.
  510. monomial_values :
  511. List of monomial values constituting the polynomial.
  512. monom_index :
  513. Index of monomial whose integration is being found.
  514. vertices : optional
  515. Coordinates of vertices constituting the 3-Polytope.
  516. hp_param : optional
  517. Hyperplane Parameter of the face of the facets[index].
  518. Examples
  519. ========
  520. >>> from sympy.abc import x, y
  521. >>> from sympy.integrals.intpoly import (integration_reduction_dynamic, \
  522. hyperplane_parameters)
  523. >>> from sympy import Point, Polygon
  524. >>> triangle = Polygon(Point(0, 3), Point(5, 3), Point(1, 1))
  525. >>> facets = triangle.sides
  526. >>> a, b = hyperplane_parameters(triangle)[0]
  527. >>> x0 = facets[0].points[0]
  528. >>> monomial_values = [[0, 0, 0, 0], [1, 0, 0, 5],\
  529. [y, 0, 1, 15], [x, 1, 0, None]]
  530. >>> integration_reduction_dynamic(facets, 0, a, b, x, 1, (x, y), 1, 0, 1,\
  531. x0, monomial_values, 3)
  532. 25/2
  533. """
  534. value = S.Zero
  535. m = len(facets)
  536. if expr == S.Zero:
  537. return expr
  538. if len(dims) == 2:
  539. if not expr.is_number:
  540. _, x_degree, y_degree, _ = monomial_values[monom_index]
  541. x_index = monom_index - max_index + \
  542. x_index - 2 if x_degree > 0 else 0
  543. y_index = monom_index - 1 if y_degree > 0 else 0
  544. x_value, y_value =\
  545. monomial_values[x_index][3], monomial_values[y_index][3]
  546. value += x_degree * x_value * x0[0] + y_degree * y_value * x0[1]
  547. value += left_integral2D(m, index, facets, x0, expr, dims)
  548. else:
  549. # For 3D use case the max_index contains the z_degree of the term
  550. z_index = max_index
  551. if not expr.is_number:
  552. x_degree, y_degree, z_degree = y_index,\
  553. z_index - x_index - y_index, x_index
  554. x_value = monomial_values[z_index - 1][y_index - 1][x_index][7]\
  555. if x_degree > 0 else 0
  556. y_value = monomial_values[z_index - 1][y_index][x_index][7]\
  557. if y_degree > 0 else 0
  558. z_value = monomial_values[z_index - 1][y_index][x_index - 1][7]\
  559. if z_degree > 0 else 0
  560. value += x_degree * x_value * x0[0] + y_degree * y_value * x0[1] \
  561. + z_degree * z_value * x0[2]
  562. value += left_integral3D(facets, index, expr,
  563. vertices, hp_param, degree)
  564. return value / (len(dims) + degree - 1)
  565. def left_integral3D(facets, index, expr, vertices, hp_param, degree):
  566. """Computes the left integral of Eq 10 in Chin et al.
  567. Explanation
  568. ===========
  569. For the 3D case, this is the sum of the integral values over constituting
  570. line segments of the face (which is accessed by facets[index]) multiplied
  571. by the distance between the first point of facet and that line segment.
  572. Parameters
  573. ==========
  574. facets :
  575. List of faces of the 3-Polytope.
  576. index :
  577. Index of face over which integral is to be calculated.
  578. expr :
  579. Input polynomial.
  580. vertices :
  581. List of vertices that constitute the 3-Polytope.
  582. hp_param :
  583. The hyperplane parameters of the face.
  584. degree :
  585. Degree of the ``expr``.
  586. Examples
  587. ========
  588. >>> from sympy.integrals.intpoly import left_integral3D
  589. >>> cube = [[(0, 0, 0), (0, 0, 5), (0, 5, 0), (0, 5, 5), (5, 0, 0),\
  590. (5, 0, 5), (5, 5, 0), (5, 5, 5)],\
  591. [2, 6, 7, 3], [3, 7, 5, 1], [7, 6, 4, 5], [1, 5, 4, 0],\
  592. [3, 1, 0, 2], [0, 4, 6, 2]]
  593. >>> facets = cube[1:]
  594. >>> vertices = cube[0]
  595. >>> left_integral3D(facets, 3, 1, vertices, ([0, -1, 0], -5), 0)
  596. -50
  597. """
  598. value = S.Zero
  599. facet = facets[index]
  600. x0 = vertices[facet[0]]
  601. facet_len = len(facet)
  602. for i, fac in enumerate(facet):
  603. side = (vertices[fac], vertices[facet[(i + 1) % facet_len]])
  604. value += distance_to_side(x0, side, hp_param[0]) * \
  605. lineseg_integrate(facet, i, side, expr, degree)
  606. return value
  607. def gradient_terms(binomial_power=0, no_of_gens=2):
  608. """Returns a list of all the possible monomials between
  609. 0 and y**binomial_power for 2D case and z**binomial_power
  610. for 3D case.
  611. Parameters
  612. ==========
  613. binomial_power :
  614. Power upto which terms are generated.
  615. no_of_gens :
  616. Denotes whether terms are being generated for 2D or 3D case.
  617. Examples
  618. ========
  619. >>> from sympy.integrals.intpoly import gradient_terms
  620. >>> gradient_terms(2)
  621. [[1, 0, 0, 0], [y, 0, 1, 0], [y**2, 0, 2, 0], [x, 1, 0, 0],
  622. [x*y, 1, 1, 0], [x**2, 2, 0, 0]]
  623. >>> gradient_terms(2, 3)
  624. [[[[1, 0, 0, 0, 0, 0, 0, 0]]], [[[y, 0, 1, 0, 1, 0, 0, 0],
  625. [z, 0, 0, 1, 1, 0, 1, 0]], [[x, 1, 0, 0, 1, 1, 0, 0]]],
  626. [[[y**2, 0, 2, 0, 2, 0, 0, 0], [y*z, 0, 1, 1, 2, 0, 1, 0],
  627. [z**2, 0, 0, 2, 2, 0, 2, 0]], [[x*y, 1, 1, 0, 2, 1, 0, 0],
  628. [x*z, 1, 0, 1, 2, 1, 1, 0]], [[x**2, 2, 0, 0, 2, 2, 0, 0]]]]
  629. """
  630. if no_of_gens == 2:
  631. count = 0
  632. terms = [None] * int((binomial_power ** 2 + 3 * binomial_power + 2) / 2)
  633. for x_count in range(0, binomial_power + 1):
  634. for y_count in range(0, binomial_power - x_count + 1):
  635. terms[count] = [x**x_count*y**y_count,
  636. x_count, y_count, 0]
  637. count += 1
  638. else:
  639. terms = [[[[x ** x_count * y ** y_count *
  640. z ** (z_count - y_count - x_count),
  641. x_count, y_count, z_count - y_count - x_count,
  642. z_count, x_count, z_count - y_count - x_count, 0]
  643. for y_count in range(z_count - x_count, -1, -1)]
  644. for x_count in range(0, z_count + 1)]
  645. for z_count in range(0, binomial_power + 1)]
  646. return terms
  647. def hyperplane_parameters(poly, vertices=None):
  648. """A helper function to return the hyperplane parameters
  649. of which the facets of the polytope are a part of.
  650. Parameters
  651. ==========
  652. poly :
  653. The input 2/3-Polytope.
  654. vertices :
  655. Vertex indices of 3-Polytope.
  656. Examples
  657. ========
  658. >>> from sympy import Point, Polygon
  659. >>> from sympy.integrals.intpoly import hyperplane_parameters
  660. >>> hyperplane_parameters(Polygon(Point(0, 3), Point(5, 3), Point(1, 1)))
  661. [((0, 1), 3), ((1, -2), -1), ((-2, -1), -3)]
  662. >>> cube = [[(0, 0, 0), (0, 0, 5), (0, 5, 0), (0, 5, 5), (5, 0, 0),\
  663. (5, 0, 5), (5, 5, 0), (5, 5, 5)],\
  664. [2, 6, 7, 3], [3, 7, 5, 1], [7, 6, 4, 5], [1, 5, 4, 0],\
  665. [3, 1, 0, 2], [0, 4, 6, 2]]
  666. >>> hyperplane_parameters(cube[1:], cube[0])
  667. [([0, -1, 0], -5), ([0, 0, -1], -5), ([-1, 0, 0], -5),
  668. ([0, 1, 0], 0), ([1, 0, 0], 0), ([0, 0, 1], 0)]
  669. """
  670. if isinstance(poly, Polygon):
  671. vertices = list(poly.vertices) + [poly.vertices[0]] # Close the polygon
  672. params = [None] * (len(vertices) - 1)
  673. for i in range(len(vertices) - 1):
  674. v1 = vertices[i]
  675. v2 = vertices[i + 1]
  676. a1 = v1[1] - v2[1]
  677. a2 = v2[0] - v1[0]
  678. b = v2[0] * v1[1] - v2[1] * v1[0]
  679. factor = gcd_list([a1, a2, b])
  680. b = S(b) / factor
  681. a = (S(a1) / factor, S(a2) / factor)
  682. params[i] = (a, b)
  683. else:
  684. params = [None] * len(poly)
  685. for i, polygon in enumerate(poly):
  686. v1, v2, v3 = [vertices[vertex] for vertex in polygon[:3]]
  687. normal = cross_product(v1, v2, v3)
  688. b = sum([normal[j] * v1[j] for j in range(0, 3)])
  689. fac = gcd_list(normal)
  690. if fac.is_zero:
  691. fac = 1
  692. normal = [j / fac for j in normal]
  693. b = b / fac
  694. params[i] = (normal, b)
  695. return params
  696. def cross_product(v1, v2, v3):
  697. """Returns the cross-product of vectors (v2 - v1) and (v3 - v1)
  698. That is : (v2 - v1) X (v3 - v1)
  699. """
  700. v2 = [v2[j] - v1[j] for j in range(0, 3)]
  701. v3 = [v3[j] - v1[j] for j in range(0, 3)]
  702. return [v3[2] * v2[1] - v3[1] * v2[2],
  703. v3[0] * v2[2] - v3[2] * v2[0],
  704. v3[1] * v2[0] - v3[0] * v2[1]]
  705. def best_origin(a, b, lineseg, expr):
  706. """Helper method for polytope_integrate. Currently not used in the main
  707. algorithm.
  708. Explanation
  709. ===========
  710. Returns a point on the lineseg whose vector inner product with the
  711. divergence of `expr` yields an expression with the least maximum
  712. total power.
  713. Parameters
  714. ==========
  715. a :
  716. Hyperplane parameter denoting direction.
  717. b :
  718. Hyperplane parameter denoting distance.
  719. lineseg :
  720. Line segment on which to find the origin.
  721. expr :
  722. The expression which determines the best point.
  723. Algorithm(currently works only for 2D use case)
  724. ===============================================
  725. 1 > Firstly, check for edge cases. Here that would refer to vertical
  726. or horizontal lines.
  727. 2 > If input expression is a polynomial containing more than one generator
  728. then find out the total power of each of the generators.
  729. x**2 + 3 + x*y + x**4*y**5 ---> {x: 7, y: 6}
  730. If expression is a constant value then pick the first boundary point
  731. of the line segment.
  732. 3 > First check if a point exists on the line segment where the value of
  733. the highest power generator becomes 0. If not check if the value of
  734. the next highest becomes 0. If none becomes 0 within line segment
  735. constraints then pick the first boundary point of the line segment.
  736. Actually, any point lying on the segment can be picked as best origin
  737. in the last case.
  738. Examples
  739. ========
  740. >>> from sympy.integrals.intpoly import best_origin
  741. >>> from sympy.abc import x, y
  742. >>> from sympy import Point, Segment2D
  743. >>> l = Segment2D(Point(0, 3), Point(1, 1))
  744. >>> expr = x**3*y**7
  745. >>> best_origin((2, 1), 3, l, expr)
  746. (0, 3.0)
  747. """
  748. a1, b1 = lineseg.points[0]
  749. def x_axis_cut(ls):
  750. """Returns the point where the input line segment
  751. intersects the x-axis.
  752. Parameters
  753. ==========
  754. ls :
  755. Line segment
  756. """
  757. p, q = ls.points
  758. if p.y.is_zero:
  759. return tuple(p)
  760. elif q.y.is_zero:
  761. return tuple(q)
  762. elif p.y/q.y < S.Zero:
  763. return p.y * (p.x - q.x)/(q.y - p.y) + p.x, S.Zero
  764. else:
  765. return ()
  766. def y_axis_cut(ls):
  767. """Returns the point where the input line segment
  768. intersects the y-axis.
  769. Parameters
  770. ==========
  771. ls :
  772. Line segment
  773. """
  774. p, q = ls.points
  775. if p.x.is_zero:
  776. return tuple(p)
  777. elif q.x.is_zero:
  778. return tuple(q)
  779. elif p.x/q.x < S.Zero:
  780. return S.Zero, p.x * (p.y - q.y)/(q.x - p.x) + p.y
  781. else:
  782. return ()
  783. gens = (x, y)
  784. power_gens = {}
  785. for i in gens:
  786. power_gens[i] = S.Zero
  787. if len(gens) > 1:
  788. # Special case for vertical and horizontal lines
  789. if len(gens) == 2:
  790. if a[0] == 0:
  791. if y_axis_cut(lineseg):
  792. return S.Zero, b/a[1]
  793. else:
  794. return a1, b1
  795. elif a[1] == 0:
  796. if x_axis_cut(lineseg):
  797. return b/a[0], S.Zero
  798. else:
  799. return a1, b1
  800. if isinstance(expr, Expr): # Find the sum total of power of each
  801. if expr.is_Add: # generator and store in a dictionary.
  802. for monomial in expr.args:
  803. if monomial.is_Pow:
  804. if monomial.args[0] in gens:
  805. power_gens[monomial.args[0]] += monomial.args[1]
  806. else:
  807. for univariate in monomial.args:
  808. term_type = len(univariate.args)
  809. if term_type == 0 and univariate in gens:
  810. power_gens[univariate] += 1
  811. elif term_type == 2 and univariate.args[0] in gens:
  812. power_gens[univariate.args[0]] +=\
  813. univariate.args[1]
  814. elif expr.is_Mul:
  815. for term in expr.args:
  816. term_type = len(term.args)
  817. if term_type == 0 and term in gens:
  818. power_gens[term] += 1
  819. elif term_type == 2 and term.args[0] in gens:
  820. power_gens[term.args[0]] += term.args[1]
  821. elif expr.is_Pow:
  822. power_gens[expr.args[0]] = expr.args[1]
  823. elif expr.is_Symbol:
  824. power_gens[expr] += 1
  825. else: # If `expr` is a constant take first vertex of the line segment.
  826. return a1, b1
  827. # TODO : This part is quite hacky. Should be made more robust with
  828. # TODO : respect to symbol names and scalable w.r.t higher dimensions.
  829. power_gens = sorted(power_gens.items(), key=lambda k: str(k[0]))
  830. if power_gens[0][1] >= power_gens[1][1]:
  831. if y_axis_cut(lineseg):
  832. x0 = (S.Zero, b / a[1])
  833. elif x_axis_cut(lineseg):
  834. x0 = (b / a[0], S.Zero)
  835. else:
  836. x0 = (a1, b1)
  837. else:
  838. if x_axis_cut(lineseg):
  839. x0 = (b/a[0], S.Zero)
  840. elif y_axis_cut(lineseg):
  841. x0 = (S.Zero, b/a[1])
  842. else:
  843. x0 = (a1, b1)
  844. else:
  845. x0 = (b/a[0])
  846. return x0
  847. def decompose(expr, separate=False):
  848. """Decomposes an input polynomial into homogeneous ones of
  849. smaller or equal degree.
  850. Explanation
  851. ===========
  852. Returns a dictionary with keys as the degree of the smaller
  853. constituting polynomials. Values are the constituting polynomials.
  854. Parameters
  855. ==========
  856. expr : Expr
  857. Polynomial(SymPy expression).
  858. separate : bool
  859. If True then simply return a list of the constituent monomials
  860. If not then break up the polynomial into constituent homogeneous
  861. polynomials.
  862. Examples
  863. ========
  864. >>> from sympy.abc import x, y
  865. >>> from sympy.integrals.intpoly import decompose
  866. >>> decompose(x**2 + x*y + x + y + x**3*y**2 + y**5)
  867. {1: x + y, 2: x**2 + x*y, 5: x**3*y**2 + y**5}
  868. >>> decompose(x**2 + x*y + x + y + x**3*y**2 + y**5, True)
  869. {x, x**2, y, y**5, x*y, x**3*y**2}
  870. """
  871. poly_dict = {}
  872. if isinstance(expr, Expr) and not expr.is_number:
  873. if expr.is_Symbol:
  874. poly_dict[1] = expr
  875. elif expr.is_Add:
  876. symbols = expr.atoms(Symbol)
  877. degrees = [(sum(degree_list(monom, *symbols)), monom)
  878. for monom in expr.args]
  879. if separate:
  880. return {monom[1] for monom in degrees}
  881. else:
  882. for monom in degrees:
  883. degree, term = monom
  884. if poly_dict.get(degree):
  885. poly_dict[degree] += term
  886. else:
  887. poly_dict[degree] = term
  888. elif expr.is_Pow:
  889. _, degree = expr.args
  890. poly_dict[degree] = expr
  891. else: # Now expr can only be of `Mul` type
  892. degree = 0
  893. for term in expr.args:
  894. term_type = len(term.args)
  895. if term_type == 0 and term.is_Symbol:
  896. degree += 1
  897. elif term_type == 2:
  898. degree += term.args[1]
  899. poly_dict[degree] = expr
  900. else:
  901. poly_dict[0] = expr
  902. if separate:
  903. return set(poly_dict.values())
  904. return poly_dict
  905. def point_sort(poly, normal=None, clockwise=True):
  906. """Returns the same polygon with points sorted in clockwise or
  907. anti-clockwise order.
  908. Note that it's necessary for input points to be sorted in some order
  909. (clockwise or anti-clockwise) for the integration algorithm to work.
  910. As a convention algorithm has been implemented keeping clockwise
  911. orientation in mind.
  912. Parameters
  913. ==========
  914. poly:
  915. 2D or 3D Polygon.
  916. normal : optional
  917. The normal of the plane which the 3-Polytope is a part of.
  918. clockwise : bool, optional
  919. Returns points sorted in clockwise order if True and
  920. anti-clockwise if False.
  921. Examples
  922. ========
  923. >>> from sympy.integrals.intpoly import point_sort
  924. >>> from sympy import Point
  925. >>> point_sort([Point(0, 0), Point(1, 0), Point(1, 1)])
  926. [Point2D(1, 1), Point2D(1, 0), Point2D(0, 0)]
  927. """
  928. pts = poly.vertices if isinstance(poly, Polygon) else poly
  929. n = len(pts)
  930. if n < 2:
  931. return list(pts)
  932. order = S.One if clockwise else S.NegativeOne
  933. dim = len(pts[0])
  934. if dim == 2:
  935. center = Point(sum((vertex.x for vertex in pts)) / n,
  936. sum((vertex.y for vertex in pts)) / n)
  937. else:
  938. center = Point(sum((vertex.x for vertex in pts)) / n,
  939. sum((vertex.y for vertex in pts)) / n,
  940. sum((vertex.z for vertex in pts)) / n)
  941. def compare(a, b):
  942. if a.x - center.x >= S.Zero and b.x - center.x < S.Zero:
  943. return -order
  944. elif a.x - center.x < 0 and b.x - center.x >= 0:
  945. return order
  946. elif a.x - center.x == 0 and b.x - center.x == 0:
  947. if a.y - center.y >= 0 or b.y - center.y >= 0:
  948. return -order if a.y > b.y else order
  949. return -order if b.y > a.y else order
  950. det = (a.x - center.x) * (b.y - center.y) -\
  951. (b.x - center.x) * (a.y - center.y)
  952. if det < 0:
  953. return -order
  954. elif det > 0:
  955. return order
  956. first = (a.x - center.x) * (a.x - center.x) +\
  957. (a.y - center.y) * (a.y - center.y)
  958. second = (b.x - center.x) * (b.x - center.x) +\
  959. (b.y - center.y) * (b.y - center.y)
  960. return -order if first > second else order
  961. def compare3d(a, b):
  962. det = cross_product(center, a, b)
  963. dot_product = sum([det[i] * normal[i] for i in range(0, 3)])
  964. if dot_product < 0:
  965. return -order
  966. elif dot_product > 0:
  967. return order
  968. return sorted(pts, key=cmp_to_key(compare if dim==2 else compare3d))
  969. def norm(point):
  970. """Returns the Euclidean norm of a point from origin.
  971. Parameters
  972. ==========
  973. point:
  974. This denotes a point in the dimension_al spac_e.
  975. Examples
  976. ========
  977. >>> from sympy.integrals.intpoly import norm
  978. >>> from sympy import Point
  979. >>> norm(Point(2, 7))
  980. sqrt(53)
  981. """
  982. half = S.Half
  983. if isinstance(point, (list, tuple)):
  984. return sum([coord ** 2 for coord in point]) ** half
  985. elif isinstance(point, Point):
  986. if isinstance(point, Point2D):
  987. return (point.x ** 2 + point.y ** 2) ** half
  988. else:
  989. return (point.x ** 2 + point.y ** 2 + point.z) ** half
  990. elif isinstance(point, dict):
  991. return sum(i**2 for i in point.values()) ** half
  992. def intersection(geom_1, geom_2, intersection_type):
  993. """Returns intersection between geometric objects.
  994. Explanation
  995. ===========
  996. Note that this function is meant for use in integration_reduction and
  997. at that point in the calling function the lines denoted by the segments
  998. surely intersect within segment boundaries. Coincident lines are taken
  999. to be non-intersecting. Also, the hyperplane intersection for 2D case is
  1000. also implemented.
  1001. Parameters
  1002. ==========
  1003. geom_1, geom_2:
  1004. The input line segments.
  1005. Examples
  1006. ========
  1007. >>> from sympy.integrals.intpoly import intersection
  1008. >>> from sympy import Point, Segment2D
  1009. >>> l1 = Segment2D(Point(1, 1), Point(3, 5))
  1010. >>> l2 = Segment2D(Point(2, 0), Point(2, 5))
  1011. >>> intersection(l1, l2, "segment2D")
  1012. (2, 3)
  1013. >>> p1 = ((-1, 0), 0)
  1014. >>> p2 = ((0, 1), 1)
  1015. >>> intersection(p1, p2, "plane2D")
  1016. (0, 1)
  1017. """
  1018. if intersection_type[:-2] == "segment":
  1019. if intersection_type == "segment2D":
  1020. x1, y1 = geom_1.points[0]
  1021. x2, y2 = geom_1.points[1]
  1022. x3, y3 = geom_2.points[0]
  1023. x4, y4 = geom_2.points[1]
  1024. elif intersection_type == "segment3D":
  1025. x1, y1, z1 = geom_1.points[0]
  1026. x2, y2, z2 = geom_1.points[1]
  1027. x3, y3, z3 = geom_2.points[0]
  1028. x4, y4, z4 = geom_2.points[1]
  1029. denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)
  1030. if denom:
  1031. t1 = x1 * y2 - y1 * x2
  1032. t2 = x3 * y4 - x4 * y3
  1033. return (S(t1 * (x3 - x4) - t2 * (x1 - x2)) / denom,
  1034. S(t1 * (y3 - y4) - t2 * (y1 - y2)) / denom)
  1035. if intersection_type[:-2] == "plane":
  1036. if intersection_type == "plane2D": # Intersection of hyperplanes
  1037. a1x, a1y = geom_1[0]
  1038. a2x, a2y = geom_2[0]
  1039. b1, b2 = geom_1[1], geom_2[1]
  1040. denom = a1x * a2y - a2x * a1y
  1041. if denom:
  1042. return (S(b1 * a2y - b2 * a1y) / denom,
  1043. S(b2 * a1x - b1 * a2x) / denom)
  1044. def is_vertex(ent):
  1045. """If the input entity is a vertex return True.
  1046. Parameter
  1047. =========
  1048. ent :
  1049. Denotes a geometric entity representing a point.
  1050. Examples
  1051. ========
  1052. >>> from sympy import Point
  1053. >>> from sympy.integrals.intpoly import is_vertex
  1054. >>> is_vertex((2, 3))
  1055. True
  1056. >>> is_vertex((2, 3, 6))
  1057. True
  1058. >>> is_vertex(Point(2, 3))
  1059. True
  1060. """
  1061. if isinstance(ent, tuple):
  1062. if len(ent) in [2, 3]:
  1063. return True
  1064. elif isinstance(ent, Point):
  1065. return True
  1066. return False
  1067. def plot_polytope(poly):
  1068. """Plots the 2D polytope using the functions written in plotting
  1069. module which in turn uses matplotlib backend.
  1070. Parameter
  1071. =========
  1072. poly:
  1073. Denotes a 2-Polytope.
  1074. """
  1075. from sympy.plotting.plot import Plot, List2DSeries
  1076. xl = [vertex.x for vertex in poly.vertices]
  1077. yl = [vertex.y for vertex in poly.vertices]
  1078. xl.append(poly.vertices[0].x) # Closing the polygon
  1079. yl.append(poly.vertices[0].y)
  1080. l2ds = List2DSeries(xl, yl)
  1081. p = Plot(l2ds, axes='label_axes=True')
  1082. p.show()
  1083. def plot_polynomial(expr):
  1084. """Plots the polynomial using the functions written in
  1085. plotting module which in turn uses matplotlib backend.
  1086. Parameter
  1087. =========
  1088. expr:
  1089. Denotes a polynomial(SymPy expression).
  1090. """
  1091. from sympy.plotting.plot import plot3d, plot
  1092. gens = expr.free_symbols
  1093. if len(gens) == 2:
  1094. plot3d(expr)
  1095. else:
  1096. plot(expr)