physdes-py 0.7__tar.gz → 0.9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. physdes_py-0.9/CHANGELOG.md +57 -0
  2. {physdes_py-0.7/src/physdes_py.egg-info → physdes_py-0.9}/PKG-INFO +1 -2
  3. {physdes_py-0.7 → physdes_py-0.9}/demo_overlap.py +0 -49
  4. {physdes_py-0.7 → physdes_py-0.9}/docs/requirements.txt +2 -0
  5. {physdes_py-0.7 → physdes_py-0.9}/example_route3d_with_constraint_and_keepouts.svg +9 -15
  6. {physdes_py-0.7 → physdes_py-0.9}/example_route3d_with_steiner_and_keepouts.svg +9 -15
  7. {physdes_py-0.7 → physdes_py-0.9}/requirements/default.txt +0 -1
  8. {physdes_py-0.7 → physdes_py-0.9}/setup.cfg +0 -3
  9. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/cts/clk_tree_vis.py +103 -18
  10. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/cts/dme_algorithm.py +38 -26
  11. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/generic.py +0 -44
  12. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/interval.py +0 -39
  13. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/manhattan_arc.py +0 -26
  14. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/point.py +0 -19
  15. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/polygon.py +5 -5
  16. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/recti.py +0 -19
  17. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/router/routing_tree.py +143 -90
  18. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/router/routing_visualizer.py +0 -15
  19. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/rpolygon.py +3 -43
  20. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/rpolygon_cut.py +125 -130
  21. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/steiner_forest/congestion_map.py +9 -8
  22. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/steiner_forest/steiner_forest_grid.py +67 -67
  23. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/vector2.py +0 -20
  24. {physdes_py-0.7 → physdes_py-0.9/src/physdes_py.egg-info}/PKG-INFO +1 -2
  25. {physdes_py-0.7 → physdes_py-0.9}/src/physdes_py.egg-info/SOURCES.txt +1 -2
  26. {physdes_py-0.7 → physdes_py-0.9}/src/physdes_py.egg-info/requires.txt +0 -1
  27. {physdes_py-0.7 → physdes_py-0.9}/src/physdes_py.egg-info/scm_file_list.json +202 -203
  28. physdes_py-0.9/src/physdes_py.egg-info/scm_version.json +8 -0
  29. {physdes_py-0.7 → physdes_py-0.9}/tests/conftest.py +13 -0
  30. physdes_py-0.9/tests/test_benchmarks.py +126 -0
  31. {physdes_py-0.7 → physdes_py-0.9}/tests/test_clk_tree_vis.py +46 -0
  32. physdes_py-0.7/.benchmarks/test_area.py +0 -29
  33. physdes_py-0.7/CHANGELOG.md +0 -16
  34. physdes_py-0.7/src/physdes/skeleton.py +0 -3
  35. physdes_py-0.7/src/physdes_py.egg-info/scm_version.json +0 -8
  36. {physdes_py-0.7 → physdes_py-0.9}/.coveragerc +0 -0
  37. {physdes_py-0.7 → physdes_py-0.9}/.flake8 +0 -0
  38. {physdes_py-0.7 → physdes_py-0.9}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  39. {physdes_py-0.7 → physdes_py-0.9}/.github/ISSUE_TEMPLATE/documentation.md +0 -0
  40. {physdes_py-0.7 → physdes_py-0.9}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  41. {physdes_py-0.7 → physdes_py-0.9}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  42. {physdes_py-0.7 → physdes_py-0.9}/.github/workflows/jekyll-gh-pages.yml +0 -0
  43. {physdes_py-0.7 → physdes_py-0.9}/.github/workflows/multi-platforms.yml +0 -0
  44. {physdes_py-0.7 → physdes_py-0.9}/.github/workflows/python-app.yml +0 -0
  45. {physdes_py-0.7 → physdes_py-0.9}/.github/workflows/python-publish.yml +0 -0
  46. {physdes_py-0.7 → physdes_py-0.9}/.gitignore +0 -0
  47. {physdes_py-0.7 → physdes_py-0.9}/.gitpod.Dockerfile +0 -0
  48. {physdes_py-0.7 → physdes_py-0.9}/.gitpod.yml +0 -0
  49. {physdes_py-0.7 → physdes_py-0.9}/.hadolint.yaml +0 -0
  50. {physdes_py-0.7 → physdes_py-0.9}/.isort.cfg +0 -0
  51. {physdes_py-0.7 → physdes_py-0.9}/.markdownlint.yaml +0 -0
  52. {physdes_py-0.7 → physdes_py-0.9}/.pre-commit-config.yaml +0 -0
  53. {physdes_py-0.7 → physdes_py-0.9}/.readthedocs.yml +0 -0
  54. {physdes_py-0.7 → physdes_py-0.9}/.shellcheckrc +0 -0
  55. {physdes_py-0.7 → physdes_py-0.9}/AGENTS.md +0 -0
  56. {physdes_py-0.7 → physdes_py-0.9}/AUTHORS.rst +0 -0
  57. {physdes_py-0.7 → physdes_py-0.9}/CHANGELOG.rst +0 -0
  58. {physdes_py-0.7 → physdes_py-0.9}/CONTRIBUTING.rst +0 -0
  59. {physdes_py-0.7 → physdes_py-0.9}/GEMINI.md +0 -0
  60. {physdes_py-0.7 → physdes_py-0.9}/LICENSE +0 -0
  61. {physdes_py-0.7 → physdes_py-0.9}/README.md +0 -0
  62. {physdes_py-0.7 → physdes_py-0.9}/_config.yml +0 -0
  63. {physdes_py-0.7 → physdes_py-0.9}/alpha10.svg +15 -15
  64. {physdes_py-0.7 → physdes_py-0.9}/alpha12.svg +12 -12
  65. {physdes_py-0.7 → physdes_py-0.9}/alpha14.svg +9 -9
  66. {physdes_py-0.7 → physdes_py-0.9}/alpha8.svg +12 -12
  67. {physdes_py-0.7 → physdes_py-0.9}/alpha9998.svg +15 -15
  68. {physdes_py-0.7 → physdes_py-0.9}/changelog/1.misc.rst +0 -0
  69. {physdes_py-0.7 → physdes_py-0.9}/changelog/README.md +0 -0
  70. {physdes_py-0.7 → physdes_py-0.9}/coverage.json +0 -0
  71. {physdes_py-0.7 → physdes_py-0.9}/demo_overlap.svg +0 -0
  72. {physdes_py-0.7 → physdes_py-0.9}/diagram.bob +0 -0
  73. {physdes_py-0.7 → physdes_py-0.9}/dme_algorithm-slides.html +0 -0
  74. {physdes_py-0.7 → physdes_py-0.9}/dme_algorithm-slides.md +0 -0
  75. {physdes_py-0.7 → physdes_py-0.9}/docs/Makefile +0 -0
  76. {physdes_py-0.7 → physdes_py-0.9}/docs/_static/.gitignore +0 -0
  77. {physdes_py-0.7 → physdes_py-0.9}/docs/authors.rst +0 -0
  78. {physdes_py-0.7 → physdes_py-0.9}/docs/build_from_md.py +0 -0
  79. {physdes_py-0.7 → physdes_py-0.9}/docs/build_remark.py +0 -0
  80. {physdes_py-0.7 → physdes_py-0.9}/docs/build_slides.py +0 -0
  81. {physdes_py-0.7 → physdes_py-0.9}/docs/changelog.rst +0 -0
  82. {physdes_py-0.7 → physdes_py-0.9}/docs/conf.py +0 -0
  83. {physdes_py-0.7 → physdes_py-0.9}/docs/contributing.md +0 -0
  84. {physdes_py-0.7 → physdes_py-0.9}/docs/contributing.rst +0 -0
  85. {physdes_py-0.7 → physdes_py-0.9}/docs/essays/essay.md +0 -0
  86. {physdes_py-0.7 → physdes_py-0.9}/docs/essays/polygon_vs_rpolygon_comparison.html +0 -0
  87. {physdes_py-0.7 → physdes_py-0.9}/docs/essays/polygon_vs_rpolygon_comparison.md +0 -0
  88. {physdes_py-0.7 → physdes_py-0.9}/docs/examples/plot_cts.py +0 -0
  89. {physdes_py-0.7 → physdes_py-0.9}/docs/examples/plot_global_router.py +0 -0
  90. {physdes_py-0.7 → physdes_py-0.9}/docs/examples/plot_rectangles.py +0 -0
  91. {physdes_py-0.7 → physdes_py-0.9}/docs/examples/plot_rpolygon.py +0 -0
  92. {physdes_py-0.7 → physdes_py-0.9}/docs/figures_demo.rst +0 -0
  93. {physdes_py-0.7 → physdes_py-0.9}/docs/index.rst +0 -0
  94. {physdes_py-0.7 → physdes_py-0.9}/docs/license.rst +0 -0
  95. {physdes_py-0.7 → physdes_py-0.9}/docs/misc/note.md +0 -0
  96. {physdes_py-0.7 → physdes_py-0.9}/docs/misc/report.md +0 -0
  97. {physdes_py-0.7 → physdes_py-0.9}/docs/my_congestion.svg +0 -0
  98. {physdes_py-0.7 → physdes_py-0.9}/docs/proposals/fyproj2026.md +0 -0
  99. {physdes_py-0.7 → physdes_py-0.9}/docs/proposals/fyproj2026.md.bad +0 -0
  100. {physdes_py-0.7 → physdes_py-0.9}/docs/proposals/fyproj2026_cts.md +0 -0
  101. {physdes_py-0.7 → physdes_py-0.9}/docs/proposals/fyproj2026_cts_2d.md +0 -0
  102. {physdes_py-0.7 → physdes_py-0.9}/docs/proposals/mulit-fpga-routing.md +0 -0
  103. {physdes_py-0.7 → physdes_py-0.9}/docs/readme.rst +0 -0
  104. {physdes_py-0.7 → physdes_py-0.9}/docs/slides_content.md +0 -0
  105. {physdes_py-0.7 → physdes_py-0.9}/docs/steiner_forest_diagonal.svg +0 -0
  106. {physdes_py-0.7 → physdes_py-0.9}/environment.yml +0 -0
  107. {physdes_py-0.7/outputs → physdes_py-0.9}/example_route_with_delay_constraint.svg +15 -15
  108. {physdes_py-0.7 → physdes_py-0.9}/example_route_with_keepouts.svg +9 -9
  109. {physdes_py-0.7/outputs → physdes_py-0.9}/example_route_with_steiner.svg +9 -9
  110. {physdes_py-0.7 → physdes_py-0.9}/example_route_with_steiner_and_keepouts.svg +9 -9
  111. {physdes_py-0.7/outputs → physdes_py-0.9}/example_route_with_steiner_special.svg +12 -12
  112. {physdes_py-0.7 → physdes_py-0.9}/examples/steiner_forest_grid_example.py +0 -0
  113. {physdes_py-0.7 → physdes_py-0.9}/examples/steiner_forest_grid_large_example.py +0 -0
  114. {physdes_py-0.7 → physdes_py-0.9}/examples/steiner_forest_simple_example.py +0 -0
  115. {physdes_py-0.7 → physdes_py-0.9}/experimental/distance_comparison.png +0 -0
  116. {physdes_py-0.7 → physdes_py-0.9}/experimental/rectilinear_distance_field.png +0 -0
  117. {physdes_py-0.7 → physdes_py-0.9}/experimental/rectilinear_voronoi.ipynb +0 -0
  118. {physdes_py-0.7 → physdes_py-0.9}/experimental/rectilinear_voronoi.py +0 -0
  119. {physdes_py-0.7 → physdes_py-0.9}/experimental/rectilinear_voronoi_diamond.png +0 -0
  120. {physdes_py-0.7 → physdes_py-0.9}/experimental/rectilinear_voronoi_grid.png +0 -0
  121. {physdes_py-0.7 → physdes_py-0.9}/experimental/rectilinear_voronoi_random.png +0 -0
  122. {physdes_py-0.7 → physdes_py-0.9}/experimental/steiner_trees_demo.py +0 -0
  123. {physdes_py-0.7 → physdes_py-0.9}/global_router-slides.html +0 -0
  124. {physdes_py-0.7 → physdes_py-0.9}/global_router-slides.md +0 -0
  125. {physdes_py-0.7 → physdes_py-0.9}/my_congestion.svg +0 -0
  126. {physdes_py-0.7 → physdes_py-0.9}/mypy.ini +0 -0
  127. {physdes_py-0.7/outputs → physdes_py-0.9}/neg_specical10.svg +27 -27
  128. {physdes_py-0.7 → physdes_py-0.9}/oryx-build-commands.txt +0 -0
  129. {physdes_py-0.7 → physdes_py-0.9}/outputs/comparison.svg +0 -0
  130. {physdes_py-0.7 → physdes_py-0.9}/outputs/convex_hull.svg +0 -0
  131. {physdes_py-0.7 → physdes_py-0.9}/outputs/delay_model_comparison.svg +0 -0
  132. {physdes_py-0.7 → physdes_py-0.9}/outputs/delay_model_comparison3d.svg +0 -0
  133. {physdes_py-0.7 → physdes_py-0.9}/outputs/elmore_model_clock_tree.svg +0 -0
  134. {physdes_py-0.7 → physdes_py-0.9}/outputs/elmore_model_clock_tree3d.svg +0 -0
  135. {physdes_py-0.7 → physdes_py-0.9}/outputs/example_route3d_with_constraint.svg +0 -0
  136. {physdes_py-0.7 → physdes_py-0.9}/outputs/example_route3d_with_constraint_and_keepouts.svg +0 -0
  137. {physdes_py-0.7 → physdes_py-0.9}/outputs/example_route3d_with_steiner.svg +0 -0
  138. {physdes_py-0.7 → physdes_py-0.9}/outputs/example_route3d_with_steiner_and_keepouts.svg +0 -0
  139. {physdes_py-0.7 → physdes_py-0.9}/outputs/example_route_with_constraint.svg +0 -0
  140. {physdes_py-0.7 → physdes_py-0.9}/outputs/example_route_with_constraint_specical.svg +0 -0
  141. {physdes_py-0.7 → physdes_py-0.9/outputs}/example_route_with_delay_constraint.svg +0 -0
  142. {physdes_py-0.7 → physdes_py-0.9}/outputs/example_route_with_keepouts.svg +0 -0
  143. {physdes_py-0.7 → physdes_py-0.9/outputs}/example_route_with_steiner.svg +0 -0
  144. {physdes_py-0.7 → physdes_py-0.9}/outputs/example_route_with_steiner_and_keepouts.svg +0 -0
  145. {physdes_py-0.7 → physdes_py-0.9/outputs}/example_route_with_steiner_special.svg +0 -0
  146. {physdes_py-0.7 → physdes_py-0.9}/outputs/linear_model_clock_tree.svg +0 -0
  147. {physdes_py-0.7 → physdes_py-0.9}/outputs/linear_model_clock_tree3d.svg +0 -0
  148. {physdes_py-0.7 → physdes_py-0.9}/outputs/my_congestion.svg +0 -0
  149. {physdes_py-0.7 → physdes_py-0.9/outputs}/neg_specical10.svg +0 -0
  150. {physdes_py-0.7 → physdes_py-0.9}/outputs/polygon_convex_hull.svg +0 -0
  151. {physdes_py-0.7 → physdes_py-0.9}/outputs/polygon_convex_hull13.svg +0 -0
  152. {physdes_py-0.7 → physdes_py-0.9}/outputs/rectilinear-shapes-for-vlsi-physical-desgin.svg +0 -0
  153. {physdes_py-0.7 → physdes_py-0.9}/outputs/rpolygon_convex_cut.svg +0 -0
  154. {physdes_py-0.7 → physdes_py-0.9}/outputs/rpolygon_cut_explicit.svg +0 -0
  155. {physdes_py-0.7 → physdes_py-0.9}/outputs/rpolyon_convex_hull.svg +0 -0
  156. {physdes_py-0.7 → physdes_py-0.9}/outputs/rpolyon_convex_hull13.svg +0 -0
  157. {physdes_py-0.7 → physdes_py-0.9}/outputs/rpolyon_cut_convex.svg +0 -0
  158. {physdes_py-0.7 → physdes_py-0.9}/outputs/rpolyon_cut_convex2.svg +0 -0
  159. {physdes_py-0.7 → physdes_py-0.9}/outputs/rpolyon_xmono.svg +0 -0
  160. {physdes_py-0.7 → physdes_py-0.9}/outputs/rpolyon_xmono_hull.svg +0 -0
  161. {physdes_py-0.7 → physdes_py-0.9}/outputs/rpolyon_ymono_hull.svg +0 -0
  162. {physdes_py-0.7 → physdes_py-0.9}/outputs/steiner_forest.svg +0 -0
  163. {physdes_py-0.7 → physdes_py-0.9}/outputs/steiner_forest_diagonal.svg +0 -0
  164. {physdes_py-0.7 → physdes_py-0.9}/outputs/test.svg +0 -0
  165. {physdes_py-0.7 → physdes_py-0.9}/outputs/xmono_hull.svg +0 -0
  166. {physdes_py-0.7 → physdes_py-0.9}/plot_alpha.py +0 -0
  167. {physdes_py-0.7 → physdes_py-0.9}/presentation_overlap_detection.html +0 -0
  168. {physdes_py-0.7 → physdes_py-0.9}/presentation_overlap_detection.md +0 -0
  169. {physdes_py-0.7 → physdes_py-0.9}/pyproject.toml +0 -0
  170. {physdes_py-0.7 → physdes_py-0.9}/requirements/README.md +0 -0
  171. {physdes_py-0.7 → physdes_py-0.9}/requirements/dev.txt +0 -0
  172. {physdes_py-0.7 → physdes_py-0.9}/requirements/doc.txt +0 -0
  173. {physdes_py-0.7 → physdes_py-0.9}/requirements/extras.txt +0 -0
  174. {physdes_py-0.7 → physdes_py-0.9}/requirements/test.txt +0 -0
  175. {physdes_py-0.7 → physdes_py-0.9}/requirements.txt +0 -0
  176. {physdes_py-0.7 → physdes_py-0.9}/rpolygon-slides.html +0 -0
  177. {physdes_py-0.7 → physdes_py-0.9}/rpolygon-slides.md +0 -0
  178. {physdes_py-0.7 → physdes_py-0.9}/setup.py +0 -0
  179. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/__init__.py +0 -0
  180. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/cts/clk_tree3d_vis.py +0 -0
  181. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/generic.tpy +0 -0
  182. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/manhattan_arc_3d.py +0 -0
  183. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/manhattan_arc_3d.tpy +0 -0
  184. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/point.tpy +0 -0
  185. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/py.typed +0 -0
  186. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/rdllist.py +0 -0
  187. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/router/global_router.py +0 -0
  188. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/router/global_router3d.tpy +0 -0
  189. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/router/routing_tree3d.tpy +0 -0
  190. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/router/steiner_forest.md +0 -0
  191. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/steiner_forest/my_congestion.svg +0 -0
  192. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/steiner_forest/steiner_forest.md +0 -0
  193. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/steiner_forest/steiner_forest.svg +0 -0
  194. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/steiner_forest/steiner_forest_diagonal.svg +0 -0
  195. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/to_polygon.py +0 -0
  196. {physdes_py-0.7 → physdes_py-0.9}/src/physdes/vector2.tpy +0 -0
  197. {physdes_py-0.7 → physdes_py-0.9}/src/physdes_py.egg-info/dependency_links.txt +0 -0
  198. {physdes_py-0.7 → physdes_py-0.9}/src/physdes_py.egg-info/not-zip-safe +0 -0
  199. {physdes_py-0.7 → physdes_py-0.9}/src/physdes_py.egg-info/top_level.txt +0 -0
  200. {physdes_py-0.7 → physdes_py-0.9}/svgo.config.js +0 -0
  201. {physdes_py-0.7 → physdes_py-0.9}/test.svg +0 -0
  202. {physdes_py-0.7 → physdes_py-0.9}/tests/__init__.py +0 -0
  203. {physdes_py-0.7 → physdes_py-0.9}/tests/golden.txt +0 -0
  204. {physdes_py-0.7 → physdes_py-0.9}/tests/mysvg.css +0 -0
  205. {physdes_py-0.7 → physdes_py-0.9}/tests/pnrpoly.svg +0 -0
  206. {physdes_py-0.7 → physdes_py-0.9}/tests/run_failing_tests.py +0 -0
  207. {physdes_py-0.7 → physdes_py-0.9}/tests/test_alpha.py +0 -0
  208. {physdes_py-0.7 → physdes_py-0.9}/tests/test_dme_algorithm.py +0 -0
  209. {physdes_py-0.7 → physdes_py-0.9}/tests/test_generic_functions.py +0 -0
  210. {physdes_py-0.7 → physdes_py-0.9}/tests/test_global_router.py +0 -0
  211. {physdes_py-0.7 → physdes_py-0.9}/tests/test_global_router3d.py +0 -0
  212. {physdes_py-0.7 → physdes_py-0.9}/tests/test_global_router3d.tpy +0 -0
  213. {physdes_py-0.7 → physdes_py-0.9}/tests/test_global_router3d_with_keepouts.py +0 -0
  214. {physdes_py-0.7 → physdes_py-0.9}/tests/test_global_router3d_with_keepouts2.py +0 -0
  215. {physdes_py-0.7 → physdes_py-0.9}/tests/test_global_router_logic.py +0 -0
  216. {physdes_py-0.7 → physdes_py-0.9}/tests/test_global_router_logic3d.tpy +0 -0
  217. {physdes_py-0.7 → physdes_py-0.9}/tests/test_global_router_with_keepouts.py +0 -0
  218. {physdes_py-0.7 → physdes_py-0.9}/tests/test_global_router_with_keepouts2.py +0 -0
  219. {physdes_py-0.7 → physdes_py-0.9}/tests/test_interval.py +0 -0
  220. {physdes_py-0.7 → physdes_py-0.9}/tests/test_manhattan_arc_3d.py +0 -0
  221. {physdes_py-0.7 → physdes_py-0.9}/tests/test_manhattan_arc_3d.tpy +0 -0
  222. {physdes_py-0.7 → physdes_py-0.9}/tests/test_merge.py +0 -0
  223. {physdes_py-0.7 → physdes_py-0.9}/tests/test_merge3d.tpy +0 -0
  224. {physdes_py-0.7 → physdes_py-0.9}/tests/test_nearest_point_to.tpy +0 -0
  225. {physdes_py-0.7 → physdes_py-0.9}/tests/test_overlap.py +0 -0
  226. {physdes_py-0.7 → physdes_py-0.9}/tests/test_point.py +0 -0
  227. {physdes_py-0.7 → physdes_py-0.9}/tests/test_point_extended.py +0 -0
  228. {physdes_py-0.7 → physdes_py-0.9}/tests/test_point_hypothesis.py +0 -0
  229. {physdes_py-0.7 → physdes_py-0.9}/tests/test_polygon.py +0 -0
  230. {physdes_py-0.7 → physdes_py-0.9}/tests/test_polygon.svg +0 -0
  231. {physdes_py-0.7 → physdes_py-0.9}/tests/test_polygon_convex_decomposition.py +0 -0
  232. {physdes_py-0.7 → physdes_py-0.9}/tests/test_polygon_hypothesis.py +0 -0
  233. {physdes_py-0.7 → physdes_py-0.9}/tests/test_rdllist.py +0 -0
  234. {physdes_py-0.7 → physdes_py-0.9}/tests/test_rect3d.py +0 -0
  235. {physdes_py-0.7 → physdes_py-0.9}/tests/test_recti.py +0 -0
  236. {physdes_py-0.7 → physdes_py-0.9}/tests/test_recti_hypothesis.py +0 -0
  237. {physdes_py-0.7 → physdes_py-0.9}/tests/test_routing_tree.py +0 -0
  238. {physdes_py-0.7 → physdes_py-0.9}/tests/test_routing_tree3d.tpy +0 -0
  239. {physdes_py-0.7 → physdes_py-0.9}/tests/test_routing_visualizer.py +0 -0
  240. {physdes_py-0.7 → physdes_py-0.9}/tests/test_rpolygon.py +0 -0
  241. {physdes_py-0.7 → physdes_py-0.9}/tests/test_rpolygon_convex_cut.py +0 -0
  242. {physdes_py-0.7 → physdes_py-0.9}/tests/test_rpolygon_convex_hull.py +0 -0
  243. {physdes_py-0.7 → physdes_py-0.9}/tests/test_rpolygon_cut.py +0 -0
  244. {physdes_py-0.7 → physdes_py-0.9}/tests/test_steiner_forest_grid.py +0 -0
  245. {physdes_py-0.7 → physdes_py-0.9}/tests/test_to_polygon.py +0 -0
  246. {physdes_py-0.7 → physdes_py-0.9}/tests/test_vector2.py +0 -0
  247. {physdes_py-0.7 → physdes_py-0.9}/tests/test_vector2_hypothesis.py +0 -0
  248. {physdes_py-0.7 → physdes_py-0.9}/tests/xmono_polygon.svg +0 -0
  249. {physdes_py-0.7 → physdes_py-0.9}/tests/xmono_polygon2.svg +0 -0
  250. {physdes_py-0.7 → physdes_py-0.9}/tests/ymono_polygon.svg +0 -0
  251. {physdes_py-0.7 → physdes_py-0.9}/tests/ymono_rpolygon.svg +0 -0
  252. {physdes_py-0.7 → physdes_py-0.9}/tox.ini +0 -0
  253. {physdes_py-0.7 → physdes_py-0.9}/wirelength_vs_alpha.png +0 -0
  254. {physdes_py-0.7 → physdes_py-0.9}/wirelength_vs_alpha_small.png +0 -0
@@ -0,0 +1,57 @@
1
+ # Changelog
2
+
3
+ ## Version 0.9 (2026-10-09)
4
+
5
+ ### Performance
6
+ - **Hot-path allocation & debug-I/O cuts**: `manhattan_arc` drops live `ic()` calls from the DME embedding path (and removes `icecream` from the runtime deps); `congestion_map` guards its demo behind `__main__`; `steiner_forest_grid` uses an iterative `UnionFind.find`, hoists finds 4→2 per edge, indexes `paid` by edge, and replaces O(|F|²) reverse-delete with union-of-paths pruning (~1.7×); `routing_tree` gets a scalar 2D fast path in `_find_insertion_point` and list-join `get_tree_structure` (~4× insertion); `dme_algorithm` precomputes keys and does a single-pass stable partition in `_build_merging_tree` (1.2–1.5×). Output-identical, verified by differential tests. (#10a4e9f)
7
+
8
+ ### Bug Fixes
9
+ - **Unbounded wirelength sentinel**: `insert_terminal_with_steiner` previously ignored the tree's `worst_wirelength` by passing a literal `10**12`; the default is now `float('inf')` and Steiner insertion passes it through, so the unconstrained variant honors any caller-set bound. (#0947827)
10
+
11
+ ### Testing & Code Quality
12
+ - **Temp-cwd isolation for tests**: Added an autouse `_isolate_cwd` fixture so visualization tests' relative-path SVG writes land in pytest's temporary directory instead of the repository root. (#409d422)
13
+ - **New benchmarks**: Added `test_benchmarks.py` (5 benchmarks). (#10a4e9f)
14
+
15
+ ### Code Cleanup
16
+ - **SVG EOF newlines**: Added trailing newlines to the example SVGs and formatted the sources. (#0c698e0)
17
+
18
+ ### Build & CI
19
+ - **Dropped `icecream` runtime dep**: Removed it from `setup.cfg` and `requirements/default.txt`. (#10a4e9f)
20
+
21
+ ## Version 0.8 (2026-09-04)
22
+
23
+ ### Features
24
+ - **Builder pattern for ClockTreeVisualizer**: Added fluent `ClockTreeVisualizerBuilder` (`builder().margin(..).node_radius(..)...build()`) mirroring the C++/Rust siblings; `create_interactive_svg` and `create_comparison_visualization` now use it. (#311848a)
25
+
26
+ ### Refactoring
27
+ - **Template Method in rpolygon cut decomposition**: Collapsed the duplicated convex/explicit recursive decomposers into one `rpolygon_cut_recur` Template Method plus shared `rpolygon_cut_impl`; public entry points unchanged. (#3666c82)
28
+ - **Factory Method in global routing tree**: Concentrated 5 duplicated ID-generation/registration sites behind a single `_create_node` factory, preserving the `insert_node_on_branch` ValueError contract and ordering constraints. (#2e03732)
29
+ - **Module-level logging**: Replaced `skeleton._logger` with standard module-level `logging`. (#6c863fd)
30
+
31
+ ### Testing & Code Quality
32
+ - **Builder tests**: Added tests for builder output, default-equivalence and fluent reuse, plus a doctest on the builder class. (#311848a)
33
+ - **mypy fixes**: Resolved None checks and type annotations. (#2a5f7b2)
34
+
35
+ ### Code Cleanup
36
+ - **Removed AI slop**: Stripped boilerplate from docstrings and comments. (#587ad95)
37
+ - **Deduped route3d legends**: Removed duplicated legend entries in example SVGs. (#678aa21)
38
+ - **Stripped empty `entry_points`**: Removed dead section from setup.cfg. (#fd06baf)
39
+ - **EOF newlines in example SVGs**: Added trailing newlines to 15 figures and formatted sources. (#23aaf16)
40
+
41
+ ### Build & CI
42
+ - **RTD doc build**: Added matplotlib and numpy to `docs/requirements.txt`. (#41cad71)
43
+
44
+ ## Version 0.7 (2026-07-16)
45
+
46
+ ### Performance
47
+ - **`__slots__` on core geometry classes**: Added `__slots__` to `Point`, `ManhattanArc`, `Polygon`, `RPolygon`, `TreeNode`, `Sink` — saving ~176 bytes per instance. Most impactful on `Point` which is used everywhere in the codebase. (#64242a6)
48
+
49
+ ### Bug Fixes
50
+ - **DME alignment with Rust**: Fixed elongation calculation with `raw_extend_left` to match Rust/C++. Replaced `sorted()` with median-partition in `build_merging_tree` (matches Rust `nth_element`). (#0267be3)
51
+ - **NodeType rename**: Changed `ALL_CAPS` enum values to `PascalCase` to align with Rust naming conventions. (#0267be3)
52
+
53
+ ### Documentation
54
+ - **Doc build scripts**: Updated remark and slide build scripts. Updated example plot scripts for RPolygon, CTS, and global router. (#0a613b3)
55
+
56
+ ### Code Cleanup
57
+ - **Removed PyScaffold boilerplate**: Deleted `skeleton.py`/`test_skeleton.py`, removed Python < 3.9 compat, unused mypy ignores, stale files. (#e594991)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: physdes-py
3
- Version: 0.7
3
+ Version: 0.9
4
4
  Summary: Physical Design Python Code
5
5
  Home-page: https://github.com/luk036/physdes-py/
6
6
  Author: Wai-Shing Luk
@@ -13,7 +13,6 @@ Classifier: Programming Language :: Python
13
13
  Description-Content-Type: text/markdown; charset=UTF-8
14
14
  Requires-Dist: typing_extensions
15
15
  Requires-Dist: mywheel
16
- Requires-Dist: icecream
17
16
  Provides-Extra: testing
18
17
  Requires-Dist: setuptools; extra == "testing"
19
18
  Requires-Dist: pytest; extra == "testing"
@@ -9,7 +9,6 @@ from typing import Generator
9
9
 
10
10
  from lds_gen.ilds import Halton
11
11
 
12
- # from physdes.interval import Interval
13
12
  from physdes.point import Point
14
13
  from physdes.recti import detect_overlap_gen
15
14
 
@@ -59,24 +58,6 @@ def visualize_overlap_svg(
59
58
  f'<svg xmlns="http://www.w3.org/2000/svg" width="{width}" height="{height}" style="background-color:#f8f9fa;">',
60
59
  ]
61
60
 
62
- # legend_y = 30
63
- # svg_parts.append(f'<text x="{margin}" y="{legend_y}" font-size="14" fill="#333">Rectangles</text>')
64
- # legend_y += 20
65
- # svg_parts.append(f'<rect x="{margin}" y="{legend_y - 12}" width="16" height="12" fill="#4a90d9" stroke="#333"/>')
66
- # svg_parts.append(f'<text x="{margin + 20}" y="{legend_y}" font-size="12" fill="#333">Non-overlapping</text>')
67
- # legend_y += 20
68
- # svg_parts.append(f'<rect x="{margin}" y="{legend_y - 12}" width="16" height="12" fill="#e74c3c" stroke="#c0392b"/>')
69
- # svg_parts.append(f'<text x="{margin + 20}" y="{legend_y}" font-size="12" fill="#333">Overlapping</text>')
70
- # if overlapping_pair:
71
- # legend_y += 20
72
- # svg_parts.append(f'<text x="{margin + 20}" y="{legend_y}" font-size="12" fill="#e74c3c">Overlap detected!</text>')
73
- # legend_y += 20
74
- # pair1_str = f"({overlapping_pair[0].xcoord}, {overlapping_pair[0].ycoord})"
75
- # svg_parts.append(f'<text x="{margin + 20}" y="{legend_y}" font-size="11" fill="#666">{pair1_str}</text>')
76
- # legend_y += 15
77
- # pair2_str = f"({overlapping_pair[1].xcoord}, {overlapping_pair[1].ycoord})"
78
- # svg_parts.append(f'<text x="{margin + 20}" y="{legend_y}" font-size="11" fill="#666">{pair2_str}</text>')
79
-
80
61
  for idx, r in enumerate(rectangles):
81
62
  x1 = to_svg_x(r.xcoord.lb)
82
63
  y1 = to_svg_y(r.ycoord.ub)
@@ -96,10 +77,6 @@ def visualize_overlap_svg(
96
77
  f'<rect x="{x1}" y="{y1}" width="{w}" height="{h}" fill="{fill}" stroke="{stroke}" stroke-width="2" opacity="{opacity}"/>'
97
78
  )
98
79
 
99
- # cx = x1 + w / 2
100
- # cy = y1 + h / 2
101
- # svg_parts.append(f'<text x="{cx}" y="{cy + 4}" text-anchor="middle" font-size="11" fill="white" font-weight="bold">{idx + 1}</text>')
102
-
103
80
  svg_parts.append("</svg>")
104
81
  return "\n".join(svg_parts)
105
82
 
@@ -111,38 +88,12 @@ def demo() -> None:
111
88
  coords = [hgen.pop() for _ in range(500)]
112
89
  svg_rects = [Point(coord[0], coord[1]).enlarge_with(10) for coord in coords]
113
90
 
114
- # svg_rects = [
115
- # Rectangle(Interval(0, 4), Interval(0, 4)),
116
- # Rectangle(Interval(2, 6), Interval(2, 6)),
117
- # Rectangle(Interval(5, 9), Interval(5, 9)),
118
- # Rectangle(Interval(8, 12), Interval(8, 12)),
119
- # Rectangle(Interval(11, 15), Interval(11, 15)),
120
- # Rectangle(Interval(3, 7), Interval(10, 14)),
121
- # Rectangle(Interval(14, 18), Interval(14, 18)),
122
- # Rectangle(Interval(16, 20), Interval(4, 8)),
123
- # Rectangle(Interval(6, 10), Interval(6, 10)),
124
- # Rectangle(Interval(9, 13), Interval(12, 16)),
125
- # Rectangle(Interval(0, 3), Interval(8, 11)),
126
- # ]
127
-
128
- # print(f"Testing with {len(svg_rects)} rectangles:")
129
- # for i, r in enumerate(svg_rects):
130
- # print(f" {i + 1}: ({r.xcoord}, {r.ycoord})")
131
-
132
91
  print()
133
92
  print("=" * 50)
134
93
  print("Overlap Detection Result")
135
94
  print("=" * 50)
136
95
  overlap_result_gen = detect_overlap_gen(svg_rects)
137
96
 
138
- # for overlap_result in overlap_result_gen:
139
- # r1, r2 = overlap_result
140
- # print("OVERLAP DETECTED between:")
141
- # print(f" Rect A: ({r1.xcoord}, {r1.ycoord})")
142
- # print(f" Rect B: ({r2.xcoord}, {r2.ycoord})")
143
- # else:
144
- # print("No overlap found")
145
-
146
97
  svg = visualize_overlap_svg(svg_rects, overlap_result_gen)
147
98
 
148
99
  with open("demo_overlap.svg", "w") as f:
@@ -1,4 +1,6 @@
1
1
  furo>=2022.09.29
2
+ matplotlib
3
+ numpy
2
4
  # Requirements file for ReadTheDocs, check .readthedocs.yml.
3
5
  # To build the module reference correctly, make sure every external package
4
6
  # under `install_requires` in `setup.cfg` is also listed here!
@@ -24,27 +24,27 @@
24
24
  <circle cx="455.64903846153845" cy="357.6923076923077" r="6" fill="green" stroke="black" stroke-width="1"/>
25
25
  <text x="463.64903846153845" y="361.6923076923077" font-family="Arial" font-size="10" fill="black">T2</text>
26
26
  <text x="455.64903846153845" y="346.6923076923077" font-family="Arial" font-size="8" fill="gray" text-anchor="middle">(377,216)</text>
27
- <circle cx="163.58173076923077" cy="396.15384615384613" r="6" fill="blue" stroke="black" stroke-width="1"/>
28
- <text x="171.58173076923077" y="400.15384615384613" font-family="Arial" font-size="10" fill="black">S1</text>
29
- <text x="163.58173076923077" y="385.15384615384613" font-family="Arial" font-size="8" fill="gray" text-anchor="middle">(-595,344)</text>
30
27
  <circle cx="236.59855769230768" cy="50.0" r="6" fill="green" stroke="black" stroke-width="1"/>
31
28
  <text x="244.59855769230768" y="54.0" font-family="Arial" font-size="10" fill="black">T3</text>
32
29
  <text x="236.59855769230768" y="39.0" font-family="Arial" font-size="8" fill="gray" text-anchor="middle">(-352,-808)</text>
33
- <circle cx="382.63221153846155" cy="357.6923076923077" r="6" fill="blue" stroke="black" stroke-width="1"/>
34
- <text x="390.63221153846155" y="361.6923076923077" font-family="Arial" font-size="10" fill="black">S2</text>
35
- <text x="382.63221153846155" y="346.6923076923077" font-family="Arial" font-size="8" fill="gray" text-anchor="middle">(134,216)</text>
30
+ <circle cx="163.58173076923077" cy="396.15384615384613" r="6" fill="blue" stroke="black" stroke-width="1"/>
31
+ <text x="171.58173076923077" y="400.15384615384613" font-family="Arial" font-size="10" fill="black">S1</text>
32
+ <text x="163.58173076923077" y="385.15384615384613" font-family="Arial" font-size="8" fill="gray" text-anchor="middle">(-595,344)</text>
36
33
  <circle cx="382.63221153846155" cy="242.3076923076923" r="6" fill="green" stroke="black" stroke-width="1"/>
37
34
  <text x="390.63221153846155" y="246.3076923076923" font-family="Arial" font-size="10" fill="black">T4</text>
38
35
  <text x="382.63221153846155" y="231.3076923076923" font-family="Arial" font-size="8" fill="gray" text-anchor="middle">(134,-168)</text>
36
+ <circle cx="382.63221153846155" cy="357.6923076923077" r="6" fill="blue" stroke="black" stroke-width="1"/>
37
+ <text x="390.63221153846155" y="361.6923076923077" font-family="Arial" font-size="10" fill="black">S2</text>
38
+ <text x="382.63221153846155" y="346.6923076923077" font-family="Arial" font-size="8" fill="gray" text-anchor="middle">(134,216)</text>
39
39
  <circle cx="601.6826923076923" cy="550.0" r="6" fill="green" stroke="black" stroke-width="1"/>
40
40
  <text x="609.6826923076923" y="554.0" font-family="Arial" font-size="10" fill="black">T5</text>
41
41
  <text x="601.6826923076923" y="539.0" font-family="Arial" font-size="8" fill="gray" text-anchor="middle">(863,856)</text>
42
- <circle cx="236.59855769230768" cy="88.46153846153845" r="6" fill="blue" stroke="black" stroke-width="1"/>
43
- <text x="244.59855769230768" y="92.46153846153845" font-family="Arial" font-size="10" fill="black">S3</text>
44
- <text x="236.59855769230768" y="77.46153846153845" font-family="Arial" font-size="8" fill="gray" text-anchor="middle">(-352,-680)</text>
45
42
  <circle cx="528.6658653846154" cy="88.46153846153845" r="6" fill="green" stroke="black" stroke-width="1"/>
46
43
  <text x="536.6658653846154" y="92.46153846153845" font-family="Arial" font-size="10" fill="black">T6</text>
47
44
  <text x="528.6658653846154" y="77.46153846153845" font-family="Arial" font-size="8" fill="gray" text-anchor="middle">(620,-680)</text>
45
+ <circle cx="236.59855769230768" cy="88.46153846153845" r="6" fill="blue" stroke="black" stroke-width="1"/>
46
+ <text x="244.59855769230768" y="92.46153846153845" font-family="Arial" font-size="10" fill="black">S3</text>
47
+ <text x="236.59855769230768" y="77.46153846153845" font-family="Arial" font-size="8" fill="gray" text-anchor="middle">(-352,-680)</text>
48
48
  <circle cx="674.6995192307692" cy="203.84615384615384" r="6" fill="green" stroke="black" stroke-width="1"/>
49
49
  <text x="682.6995192307692" y="207.84615384615384" font-family="Arial" font-size="10" fill="black">T7</text>
50
50
  <text x="674.6995192307692" y="192.84615384615384" font-family="Arial" font-size="8" fill="gray" text-anchor="middle">(1106,-296)</text>
@@ -57,12 +57,6 @@
57
57
  <text x="30" y="60" font-family="Arial" font-size="10">Steiner</text>
58
58
  <circle cx="20" cy="76" r="4" fill="green" stroke="black"/>
59
59
  <text x="30" y="80" font-family="Arial" font-size="10">Terminal</text>
60
- <circle cx="20" cy="36" r="4" fill="red" stroke="black"/>
61
- <text x="30" y="40" font-family="Arial" font-size="10">Source</text>
62
- <circle cx="20" cy="56" r="4" fill="blue" stroke="black"/>
63
- <text x="30" y="60" font-family="Arial" font-size="10">Steiner</text>
64
- <circle cx="20" cy="76" r="4" fill="green" stroke="black"/>
65
- <text x="30" y="80" font-family="Arial" font-size="10">Terminal</text>
66
60
  <text x="20" y="110" font-family="Arial" font-size="10" font-weight="bold">Statistics:</text>
67
61
  <text x="20" y="125" font-family="Arial" font-size="9">Total Nodes: 11</text>
68
62
  <text x="20" y="140" font-family="Arial" font-size="9">Terminals: 7</text>
@@ -24,27 +24,27 @@
24
24
  <circle cx="455.64903846153845" cy="357.6923076923077" r="6" fill="green" stroke="black" stroke-width="1"/>
25
25
  <text x="463.64903846153845" y="361.6923076923077" font-family="Arial" font-size="10" fill="black">T2</text>
26
26
  <text x="455.64903846153845" y="346.6923076923077" font-family="Arial" font-size="8" fill="gray" text-anchor="middle">(377,216)</text>
27
- <circle cx="163.58173076923077" cy="396.15384615384613" r="6" fill="blue" stroke="black" stroke-width="1"/>
28
- <text x="171.58173076923077" y="400.15384615384613" font-family="Arial" font-size="10" fill="black">S1</text>
29
- <text x="163.58173076923077" y="385.15384615384613" font-family="Arial" font-size="8" fill="gray" text-anchor="middle">(-595,344)</text>
30
27
  <circle cx="236.59855769230768" cy="50.0" r="6" fill="green" stroke="black" stroke-width="1"/>
31
28
  <text x="244.59855769230768" y="54.0" font-family="Arial" font-size="10" fill="black">T3</text>
32
29
  <text x="236.59855769230768" y="39.0" font-family="Arial" font-size="8" fill="gray" text-anchor="middle">(-352,-808)</text>
33
- <circle cx="382.63221153846155" cy="357.6923076923077" r="6" fill="blue" stroke="black" stroke-width="1"/>
34
- <text x="390.63221153846155" y="361.6923076923077" font-family="Arial" font-size="10" fill="black">S2</text>
35
- <text x="382.63221153846155" y="346.6923076923077" font-family="Arial" font-size="8" fill="gray" text-anchor="middle">(134,216)</text>
30
+ <circle cx="163.58173076923077" cy="396.15384615384613" r="6" fill="blue" stroke="black" stroke-width="1"/>
31
+ <text x="171.58173076923077" y="400.15384615384613" font-family="Arial" font-size="10" fill="black">S1</text>
32
+ <text x="163.58173076923077" y="385.15384615384613" font-family="Arial" font-size="8" fill="gray" text-anchor="middle">(-595,344)</text>
36
33
  <circle cx="382.63221153846155" cy="242.3076923076923" r="6" fill="green" stroke="black" stroke-width="1"/>
37
34
  <text x="390.63221153846155" y="246.3076923076923" font-family="Arial" font-size="10" fill="black">T4</text>
38
35
  <text x="382.63221153846155" y="231.3076923076923" font-family="Arial" font-size="8" fill="gray" text-anchor="middle">(134,-168)</text>
36
+ <circle cx="382.63221153846155" cy="357.6923076923077" r="6" fill="blue" stroke="black" stroke-width="1"/>
37
+ <text x="390.63221153846155" y="361.6923076923077" font-family="Arial" font-size="10" fill="black">S2</text>
38
+ <text x="382.63221153846155" y="346.6923076923077" font-family="Arial" font-size="8" fill="gray" text-anchor="middle">(134,216)</text>
39
39
  <circle cx="601.6826923076923" cy="550.0" r="6" fill="green" stroke="black" stroke-width="1"/>
40
40
  <text x="609.6826923076923" y="554.0" font-family="Arial" font-size="10" fill="black">T5</text>
41
41
  <text x="601.6826923076923" y="539.0" font-family="Arial" font-size="8" fill="gray" text-anchor="middle">(863,856)</text>
42
- <circle cx="236.59855769230768" cy="88.46153846153845" r="6" fill="blue" stroke="black" stroke-width="1"/>
43
- <text x="244.59855769230768" y="92.46153846153845" font-family="Arial" font-size="10" fill="black">S3</text>
44
- <text x="236.59855769230768" y="77.46153846153845" font-family="Arial" font-size="8" fill="gray" text-anchor="middle">(-352,-680)</text>
45
42
  <circle cx="528.6658653846154" cy="88.46153846153845" r="6" fill="green" stroke="black" stroke-width="1"/>
46
43
  <text x="536.6658653846154" y="92.46153846153845" font-family="Arial" font-size="10" fill="black">T6</text>
47
44
  <text x="528.6658653846154" y="77.46153846153845" font-family="Arial" font-size="8" fill="gray" text-anchor="middle">(620,-680)</text>
45
+ <circle cx="236.59855769230768" cy="88.46153846153845" r="6" fill="blue" stroke="black" stroke-width="1"/>
46
+ <text x="244.59855769230768" y="92.46153846153845" font-family="Arial" font-size="10" fill="black">S3</text>
47
+ <text x="236.59855769230768" y="77.46153846153845" font-family="Arial" font-size="8" fill="gray" text-anchor="middle">(-352,-680)</text>
48
48
  <circle cx="674.6995192307692" cy="203.84615384615384" r="6" fill="green" stroke="black" stroke-width="1"/>
49
49
  <text x="682.6995192307692" y="207.84615384615384" font-family="Arial" font-size="10" fill="black">T7</text>
50
50
  <text x="674.6995192307692" y="192.84615384615384" font-family="Arial" font-size="8" fill="gray" text-anchor="middle">(1106,-296)</text>
@@ -57,12 +57,6 @@
57
57
  <text x="30" y="60" font-family="Arial" font-size="10">Steiner</text>
58
58
  <circle cx="20" cy="76" r="4" fill="green" stroke="black"/>
59
59
  <text x="30" y="80" font-family="Arial" font-size="10">Terminal</text>
60
- <circle cx="20" cy="36" r="4" fill="red" stroke="black"/>
61
- <text x="30" y="40" font-family="Arial" font-size="10">Source</text>
62
- <circle cx="20" cy="56" r="4" fill="blue" stroke="black"/>
63
- <text x="30" y="60" font-family="Arial" font-size="10">Steiner</text>
64
- <circle cx="20" cy="76" r="4" fill="green" stroke="black"/>
65
- <text x="30" y="80" font-family="Arial" font-size="10">Terminal</text>
66
60
  <text x="20" y="110" font-family="Arial" font-size="10" font-weight="bold">Statistics:</text>
67
61
  <text x="20" y="125" font-family="Arial" font-size="9">Total Nodes: 11</text>
68
62
  <text x="20" y="140" font-family="Arial" font-size="9">Terminals: 7</text>
@@ -1,4 +1,3 @@
1
1
  decorator>=4.1.0
2
- icecream>=1.0
3
2
  mywheel>=0.2
4
3
  typing_extensions>=4.5
@@ -24,7 +24,6 @@ package_dir =
24
24
  install_requires =
25
25
  typing_extensions
26
26
  mywheel
27
- icecream
28
27
 
29
28
  [options.packages.find]
30
29
  where = src
@@ -50,8 +49,6 @@ dev =
50
49
  safety
51
50
  towncrier
52
51
 
53
- [options.entry_points]
54
-
55
52
  [tool:pytest]
56
53
  addopts =
57
54
  --verbose
@@ -14,6 +14,84 @@ from physdes.cts.dme_algorithm import Sink
14
14
  from physdes.point import Point
15
15
 
16
16
 
17
+ class ClockTreeVisualizerBuilder:
18
+ """
19
+ Fluent builder for configuring a ClockTreeVisualizer.
20
+
21
+ Unset options fall back to the same defaults as ``ClockTreeVisualizer()``.
22
+
23
+ Examples:
24
+ >>> viz = ClockTreeVisualizerBuilder().margin(10).node_radius(5).build()
25
+ >>> viz.margin
26
+ 10
27
+ >>> viz.node_radius
28
+ 5
29
+ """
30
+
31
+ def __init__(self) -> None:
32
+ self._margin = 50
33
+ self._node_radius = 8
34
+ self._wire_width = 2
35
+ self._sink_color = "#4CAF50"
36
+ self._internal_color = "#2196F3"
37
+ self._root_color = "#F44336"
38
+ self._wire_color = "#666666"
39
+ self._text_color = "#333333"
40
+
41
+ def margin(self, value: int) -> "ClockTreeVisualizerBuilder":
42
+ """Set the margin around the drawing."""
43
+ self._margin = value
44
+ return self
45
+
46
+ def node_radius(self, value: int) -> "ClockTreeVisualizerBuilder":
47
+ """Set the radius of node circles."""
48
+ self._node_radius = value
49
+ return self
50
+
51
+ def wire_width(self, value: int) -> "ClockTreeVisualizerBuilder":
52
+ """Set the width of wire lines."""
53
+ self._wire_width = value
54
+ return self
55
+
56
+ def sink_color(self, value: str) -> "ClockTreeVisualizerBuilder":
57
+ """Set the color for sink nodes."""
58
+ self._sink_color = value
59
+ return self
60
+
61
+ def internal_color(self, value: str) -> "ClockTreeVisualizerBuilder":
62
+ """Set the color for internal nodes."""
63
+ self._internal_color = value
64
+ return self
65
+
66
+ def root_color(self, value: str) -> "ClockTreeVisualizerBuilder":
67
+ """Set the color for the root node."""
68
+ self._root_color = value
69
+ return self
70
+
71
+ def wire_color(self, value: str) -> "ClockTreeVisualizerBuilder":
72
+ """Set the color for wires."""
73
+ self._wire_color = value
74
+ return self
75
+
76
+ def text_color(self, value: str) -> "ClockTreeVisualizerBuilder":
77
+ """Set the color for text labels."""
78
+ self._text_color = value
79
+ return self
80
+
81
+ def build(self) -> "ClockTreeVisualizer":
82
+ """Build the configured ClockTreeVisualizer."""
83
+ return ClockTreeVisualizer(
84
+ margin=self._margin,
85
+ node_radius=self._node_radius,
86
+ wire_width=self._wire_width,
87
+ sink_color=self._sink_color,
88
+ internal_color=self._internal_color,
89
+ root_color=self._root_color,
90
+ wire_color=self._wire_color,
91
+ text_color=self._text_color,
92
+ )
93
+
94
+
17
95
  class ClockTreeVisualizer:
18
96
  """Visualizes clock trees in SVG format"""
19
97
 
@@ -57,6 +135,11 @@ class ClockTreeVisualizer:
57
135
  self.wire_color = wire_color
58
136
  self.text_color = text_color
59
137
 
138
+ @classmethod
139
+ def builder(cls) -> ClockTreeVisualizerBuilder:
140
+ """Return a fluent builder for customizing the visualizer appearance."""
141
+ return ClockTreeVisualizerBuilder()
142
+
60
143
  def visualize_tree(
61
144
  self,
62
145
  root: Any,
@@ -344,15 +427,17 @@ def create_interactive_svg(
344
427
  SVG string content
345
428
  """
346
429
 
347
- visualizer = ClockTreeVisualizer(
348
- margin=60,
349
- node_radius=10,
350
- wire_width=3,
351
- sink_color="#2E7D32",
352
- internal_color="#1565C0",
353
- root_color="#C62828",
354
- wire_color="#455A64",
355
- text_color="#263238",
430
+ visualizer = (
431
+ ClockTreeVisualizer.builder()
432
+ .margin(60)
433
+ .node_radius(10)
434
+ .wire_width(3)
435
+ .sink_color("#2E7D32")
436
+ .internal_color("#1565C0")
437
+ .root_color("#C62828")
438
+ .wire_color("#455A64")
439
+ .text_color("#263238")
440
+ .build()
356
441
  )
357
442
 
358
443
  svg_content = visualizer.visualize_tree(
@@ -421,13 +506,15 @@ def create_comparison_visualization(
421
506
  '<rect width="100%" height="100%" fill="white"/>',
422
507
  ]
423
508
 
424
- visualizer = ClockTreeVisualizer(
425
- margin=40,
426
- node_radius=6,
427
- wire_width=2,
428
- sink_color="#4CAF50",
429
- internal_color="#2196F3",
430
- root_color="#F44336",
509
+ visualizer = (
510
+ ClockTreeVisualizer.builder()
511
+ .margin(40)
512
+ .node_radius(6)
513
+ .wire_width(2)
514
+ .sink_color("#4CAF50")
515
+ .internal_color("#2196F3")
516
+ .root_color("#F44336")
517
+ .build()
431
518
  )
432
519
 
433
520
  for i, tree_data in enumerate(trees_data):
@@ -539,8 +626,6 @@ def visualize_example_tree() -> Tuple[str, str, str]:
539
626
  LinearDelayCalculator,
540
627
  )
541
628
 
542
- # coords = generate_random_points_for_sinks()
543
- # example_sinks = [Sink(f"s{i}", Point(x * 100, y * 100), 1.0) for i, (x, y) in enumerate(coords)]
544
629
  # Generate example clock tree with both delay models
545
630
  example_sinks = [
546
631
  Sink("s1", Point(-100, 40), 1.0),
@@ -22,13 +22,15 @@ Key components of the module include:
22
22
  """
23
23
 
24
24
  import doctest
25
+ import logging
25
26
  from abc import ABC, abstractmethod
26
27
  from typing import Any, Dict, List, Optional, Tuple, Type, Union
27
28
 
28
29
  from physdes.manhattan_arc import ManhattanArc
29
30
  from physdes.manhattan_arc_3d import ManhattanArc3D
30
31
  from physdes.point import Point
31
- from physdes.skeleton import _logger
32
+
33
+ _logger = logging.getLogger(__name__)
32
34
 
33
35
 
34
36
  class Sink:
@@ -74,7 +76,7 @@ class TreeNode:
74
76
  self.left = left
75
77
  self.right = right
76
78
  self.parent = parent
77
- self.segment = None # merging segment, set during DME
79
+ self.segment: Any = None # merging segment, set during DME
78
80
  self.wire_length = wire_length
79
81
  self.delay = delay
80
82
  self.capacitance = capacitance
@@ -374,9 +376,7 @@ class DMEAlgorithm:
374
376
  ) -> "TreeNode":
375
377
  """Build balanced merging tree using median-partition (like Rust's select_nth_unstable_by)."""
376
378
  if hi is None:
377
- nodes.sort(
378
- key=lambda n: n.position.ycoord
379
- )
379
+ nodes.sort(key=lambda n: n.position.ycoord)
380
380
  hi = len(nodes)
381
381
 
382
382
  count = hi - lo
@@ -386,21 +386,25 @@ class DMEAlgorithm:
386
386
  raise ValueError("Empty node range")
387
387
 
388
388
  mid = lo + count // 2
389
- key_fn = (lambda n: n.position.xcoord) if vertical else (lambda n: n.position.ycoord)
390
389
 
391
- # Median-partition in O(n): like Rust's select_nth_unstable_by / C++ nth_element
392
390
  sub = nodes[lo:hi]
393
- values = sorted([key_fn(n) for n in sub])
394
- median_val = values[len(values) // 2]
395
- lt = [n for n in sub if key_fn(n) < median_val]
396
- eq = [n for n in sub if key_fn(n) == median_val]
397
- gt = [n for n in sub if key_fn(n) > median_val]
398
- need = count // 2 - len(lt)
399
- if need > 0:
400
- lt.extend(eq[:need])
401
- eq = eq[need:]
402
- gt = eq + gt
403
- nodes[lo:hi] = lt + gt
391
+ keys = (
392
+ [n.position.xcoord for n in sub]
393
+ if vertical
394
+ else [n.position.ycoord for n in sub]
395
+ )
396
+ median_val = sorted(keys)[count // 2]
397
+ lt: List["TreeNode"] = []
398
+ eq: List["TreeNode"] = []
399
+ gt: List["TreeNode"] = []
400
+ for node, value in zip(sub, keys):
401
+ if value < median_val:
402
+ lt.append(node)
403
+ elif value == median_val:
404
+ eq.append(node)
405
+ else:
406
+ gt.append(node)
407
+ nodes[lo:hi] = lt + eq + gt
404
408
 
405
409
  left_child = self._build_merging_tree(nodes, not vertical, lo, mid)
406
410
  right_child = self._build_merging_tree(nodes, not vertical, mid, hi)
@@ -441,11 +445,16 @@ class DMEAlgorithm:
441
445
  distance = left_ms.min_dist_with(right_ms) # type: ignore[arg-type]
442
446
 
443
447
  # Pure calculator: no node mutation inside
444
- (extend_left, raw_extend_left, delay_left) = (
445
- self.delay_calculator.calculate_tapping_point(
446
- distance, node.left.delay, node.right.delay,
447
- node.left.capacitance, node.right.capacitance,
448
- )
448
+ (
449
+ extend_left,
450
+ raw_extend_left,
451
+ delay_left,
452
+ ) = self.delay_calculator.calculate_tapping_point(
453
+ distance,
454
+ node.left.delay,
455
+ node.right.delay,
456
+ node.left.capacitance,
457
+ node.right.capacitance,
449
458
  )
450
459
 
451
460
  # Apply elongation logic (matches Rust/C++ TappingResult handling)
@@ -500,15 +509,18 @@ class DMEAlgorithm:
500
509
 
501
510
  if parent_segment is None:
502
511
  node_segment = node.segment
512
+ if node_segment is None:
513
+ return
503
514
  if self.source is None:
504
515
  node.position = node_segment.get_upper_corner()
505
516
  else:
506
517
  node.position = node_segment.nearest_point_to(self.source)
507
518
  else:
508
519
  node_segment = node.segment
509
- if node.parent:
510
- node.position = node_segment.nearest_point_to(node.parent.position)
511
- node.wire_length = node.position.min_dist_with(node.parent.position)
520
+ if node_segment is None or node.parent is None:
521
+ return
522
+ node.position = node_segment.nearest_point_to(node.parent.position)
523
+ node.wire_length = node.position.min_dist_with(node.parent.position)
512
524
 
513
525
  embed_node(node.left, node.segment)
514
526
  embed_node(node.right, node.segment)
@@ -234,50 +234,6 @@ def nearest(lhs: Any, rhs: Any) -> Any:
234
234
  return lhs
235
235
 
236
236
 
237
- # def min_dist_change(lhs, rhs):
238
- # """
239
- # The `min_dist_change` function calculates the minimum Manhattan distance change between two objects.
240
- #
241
- # :param lhs: The `lhs` parameter represents the left-hand side value or object that you want to
242
- # compare
243
- # :param rhs: The `rhs` parameter represents the right-hand side value or object that we want to
244
- # compare with the `lhs` parameter
245
- # :return: The function `min_dist_change` returns the minimum Manhattan distance change between `lhs` and `rhs`.
246
- #
247
- # Examples:
248
- # >>> min_dist_change(1, 1)
249
- # 0
250
- # >>> min_dist_change(1, 3)
251
- # 2
252
- # >>> min_dist_change(Interval(1, 2), Interval(2, 3))
253
- # 0
254
- # >>> min_dist_change(Interval(1, 2), Interval(3, 4))
255
- # 1
256
- # >>> min_dist_change(Interval(1, 2), 2)
257
- # 0
258
- # >>> min_dist_change(Interval(1, 2), 4)
259
- # 2
260
- # >>> min_dist_change(2, Interval(2, 3))
261
- # 0
262
- # >>> min_dist_change(1, Interval(3, 4))
263
- # 2
264
- # >>> min_dist_change(1, Interval(1, 2))
265
- # 0
266
- # >>> min_dist_change(Interval(1, 2), Interval(1, 2))
267
- # 0
268
- # >>> min_dist_change(Interval(1, 2), Interval(2, 3))
269
- # 0
270
- # >>> min_dist_change(Interval(1, 2), 2)
271
- # 0
272
- # """
273
- # if hasattr(lhs, "min_dist_change_with"):
274
- # return lhs.min_dist_change_with(rhs)
275
- # elif hasattr(rhs, "min_dist_change_with"):
276
- # return rhs.min_dist_change_with(lhs)
277
- # else: # assume scalar
278
- # return abs(lhs - rhs)
279
-
280
-
281
237
  def measure_of(obj: Any) -> Union[int, Any]:
282
238
  """
283
239
  The `measure_of` function calculates the measure of an object.