pytensor 3.0.1__tar.gz → 3.1.0__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 (597) hide show
  1. {pytensor-3.0.1/pytensor.egg-info → pytensor-3.1.0}/PKG-INFO +1 -1
  2. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/_version.py +3 -3
  3. pytensor-3.1.0/pytensor/assumptions/__init__.py +88 -0
  4. pytensor-3.1.0/pytensor/assumptions/alloc.py +90 -0
  5. pytensor-3.1.0/pytensor/assumptions/blockwise.py +17 -0
  6. pytensor-3.1.0/pytensor/assumptions/core.py +373 -0
  7. pytensor-3.1.0/pytensor/assumptions/diagonal.py +134 -0
  8. pytensor-3.1.0/pytensor/assumptions/dimshuffle.py +33 -0
  9. pytensor-3.1.0/pytensor/assumptions/dot.py +58 -0
  10. pytensor-3.1.0/pytensor/assumptions/elemwise.py +84 -0
  11. pytensor-3.1.0/pytensor/assumptions/orthogonal.py +85 -0
  12. pytensor-3.1.0/pytensor/assumptions/positive_definite.py +167 -0
  13. pytensor-3.1.0/pytensor/assumptions/reshape.py +28 -0
  14. pytensor-3.1.0/pytensor/assumptions/shape.py +30 -0
  15. pytensor-3.1.0/pytensor/assumptions/specify.py +130 -0
  16. pytensor-3.1.0/pytensor/assumptions/subtensor.py +75 -0
  17. pytensor-3.1.0/pytensor/assumptions/symmetric.py +118 -0
  18. pytensor-3.1.0/pytensor/assumptions/triangular.py +167 -0
  19. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/compile/executor.py +16 -25
  20. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/compile/maker.py +0 -19
  21. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/configdefaults.py +2 -9
  22. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/graph/basic.py +1 -2
  23. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/graph/fg.py +13 -7
  24. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/graph/rewriting/utils.py +1 -1
  25. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/jax/linker.py +1 -1
  26. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/mlx/linker.py +1 -1
  27. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/basic.py +1 -1
  28. pytensor-3.1.0/pytensor/link/numba/dispatch/elemwise.py +948 -0
  29. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/linalg/_LAPACK.py +203 -0
  30. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/linalg/__init__.py +1 -0
  31. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/linalg/decomposition/dispatch.py +67 -55
  32. pytensor-3.1.0/pytensor/link/numba/dispatch/linalg/decomposition/svd.py +719 -0
  33. pytensor-3.1.0/pytensor/link/numba/dispatch/linalg/products.py +330 -0
  34. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/linalg/solvers/dispatch.py +10 -5
  35. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/scan.py +1 -1
  36. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/linker.py +1 -1
  37. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/pytorch/linker.py +1 -1
  38. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/utils.py +6 -0
  39. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/printing.py +41 -2
  40. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/scalar/basic.py +4 -25
  41. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/scalar/loop.py +4 -20
  42. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/scan/op.py +11 -10
  43. pytensor-3.1.0/pytensor/scan/rewriting/__init__.py +25 -0
  44. pytensor-3.1.0/pytensor/scan/rewriting/db.py +236 -0
  45. pytensor-3.1.0/pytensor/scan/rewriting/inplace.py +190 -0
  46. pytensor-3.1.0/pytensor/scan/rewriting/io.py +291 -0
  47. pytensor-3.1.0/pytensor/scan/rewriting/merge.py +545 -0
  48. pytensor-3.1.0/pytensor/scan/rewriting/push_out.py +961 -0
  49. pytensor-3.1.0/pytensor/scan/rewriting/trace.py +1078 -0
  50. pytensor-3.1.0/pytensor/scan/rewriting/utils.py +145 -0
  51. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/sparse/basic.py +1 -1
  52. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/basic.py +9 -0
  53. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/extra_ops.py +9 -6
  54. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/linalg/decomposition/svd.py +21 -2
  55. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/linalg/products.py +16 -2
  56. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/rewriting/__init__.py +1 -0
  57. pytensor-3.1.0/pytensor/tensor/rewriting/assumptions.py +64 -0
  58. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/rewriting/basic.py +24 -0
  59. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/rewriting/elemwise.py +38 -47
  60. pytensor-3.1.0/pytensor/tensor/rewriting/linalg/decomposition.py +494 -0
  61. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/rewriting/linalg/inverse.py +38 -52
  62. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/rewriting/linalg/products.py +75 -3
  63. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/rewriting/linalg/solvers.py +169 -9
  64. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/rewriting/linalg/summary.py +17 -50
  65. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/rewriting/linalg/utils.py +37 -1
  66. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/rewriting/math.py +42 -3
  67. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/rewriting/ofg.py +7 -10
  68. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/rewriting/subtensor.py +912 -203
  69. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/rewriting/subtensor_lift.py +390 -72
  70. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/special.py +3 -0
  71. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/subtensor.py +105 -5
  72. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/variable.py +4 -0
  73. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/xtensor/basic.py +3 -0
  74. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/xtensor/linalg.py +4 -0
  75. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/xtensor/type.py +10 -0
  76. {pytensor-3.0.1 → pytensor-3.1.0/pytensor.egg-info}/PKG-INFO +1 -1
  77. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor.egg-info/SOURCES.txt +27 -1
  78. {pytensor-3.0.1 → pytensor-3.1.0}/scripts/mypy-failing.txt +0 -1
  79. {pytensor-3.0.1 → pytensor-3.1.0}/tests/test_printing.py +71 -0
  80. pytensor-3.0.1/pytensor/link/numba/dispatch/elemwise.py +0 -638
  81. pytensor-3.0.1/pytensor/scan/rewriting.py +0 -3140
  82. pytensor-3.0.1/pytensor/tensor/rewriting/linalg/decomposition.py +0 -134
  83. {pytensor-3.0.1 → pytensor-3.1.0}/LICENSE.txt +0 -0
  84. {pytensor-3.0.1 → pytensor-3.1.0}/MANIFEST.in +0 -0
  85. {pytensor-3.0.1 → pytensor-3.1.0}/README.rst +0 -0
  86. {pytensor-3.0.1 → pytensor-3.1.0}/doc/.templates/PLACEHOLDER +0 -0
  87. {pytensor-3.0.1 → pytensor-3.1.0}/doc/.templates/layout.html +0 -0
  88. {pytensor-3.0.1 → pytensor-3.1.0}/doc/.templates/nb-badges.html +0 -0
  89. {pytensor-3.0.1 → pytensor-3.1.0}/doc/.templates/rendered_citation.html +0 -0
  90. {pytensor-3.0.1 → pytensor-3.1.0}/doc/LICENSE.txt +0 -0
  91. {pytensor-3.0.1 → pytensor-3.1.0}/doc/README.md +0 -0
  92. {pytensor-3.0.1 → pytensor-3.1.0}/doc/_drafts/benchmark_mlx_v_jax_corrected.ipynb +0 -0
  93. {pytensor-3.0.1 → pytensor-3.1.0}/doc/_thumbnails/autodiff/vector_jacobian_product.png +0 -0
  94. {pytensor-3.0.1 → pytensor-3.1.0}/doc/acknowledgement.rst +0 -0
  95. {pytensor-3.0.1 → pytensor-3.1.0}/doc/bcast.png +0 -0
  96. {pytensor-3.0.1 → pytensor-3.1.0}/doc/bcast.svg +0 -0
  97. {pytensor-3.0.1 → pytensor-3.1.0}/doc/blog.md +0 -0
  98. {pytensor-3.0.1 → pytensor-3.1.0}/doc/conf.py +0 -0
  99. {pytensor-3.0.1 → pytensor-3.1.0}/doc/core_development_guide.rst +0 -0
  100. {pytensor-3.0.1 → pytensor-3.1.0}/doc/css.inc +0 -0
  101. {pytensor-3.0.1 → pytensor-3.1.0}/doc/dev_start_guide.rst +0 -0
  102. {pytensor-3.0.1 → pytensor-3.1.0}/doc/environment.yml +0 -0
  103. {pytensor-3.0.1 → pytensor-3.1.0}/doc/extending/apply.png +0 -0
  104. {pytensor-3.0.1 → pytensor-3.1.0}/doc/extending/apply.svg +0 -0
  105. {pytensor-3.0.1 → pytensor-3.1.0}/doc/extending/apply2.svg +0 -0
  106. {pytensor-3.0.1 → pytensor-3.1.0}/doc/extending/creating_a_c_op.rst +0 -0
  107. {pytensor-3.0.1 → pytensor-3.1.0}/doc/extending/creating_a_numba_jax_op.rst +0 -0
  108. {pytensor-3.0.1 → pytensor-3.1.0}/doc/extending/creating_an_op.rst +0 -0
  109. {pytensor-3.0.1 → pytensor-3.1.0}/doc/extending/ctype.rst +0 -0
  110. {pytensor-3.0.1 → pytensor-3.1.0}/doc/extending/extending_faq.rst +0 -0
  111. {pytensor-3.0.1 → pytensor-3.1.0}/doc/extending/extending_pytensor_solution_1.py +0 -0
  112. {pytensor-3.0.1 → pytensor-3.1.0}/doc/extending/graph_rewriting.rst +0 -0
  113. {pytensor-3.0.1 → pytensor-3.1.0}/doc/extending/graphstructures.rst +0 -0
  114. {pytensor-3.0.1 → pytensor-3.1.0}/doc/extending/index.rst +0 -0
  115. {pytensor-3.0.1 → pytensor-3.1.0}/doc/extending/inplace.rst +0 -0
  116. {pytensor-3.0.1 → pytensor-3.1.0}/doc/extending/op.rst +0 -0
  117. {pytensor-3.0.1 → pytensor-3.1.0}/doc/extending/other_ops.rst +0 -0
  118. {pytensor-3.0.1 → pytensor-3.1.0}/doc/extending/pics/symbolic_graph_opt.png +0 -0
  119. {pytensor-3.0.1 → pytensor-3.1.0}/doc/extending/pics/symbolic_graph_unopt.png +0 -0
  120. {pytensor-3.0.1 → pytensor-3.1.0}/doc/extending/pipeline.rst +0 -0
  121. {pytensor-3.0.1 → pytensor-3.1.0}/doc/extending/scan.rst +0 -0
  122. {pytensor-3.0.1 → pytensor-3.1.0}/doc/extending/tips.rst +0 -0
  123. {pytensor-3.0.1 → pytensor-3.1.0}/doc/extending/type.rst +0 -0
  124. {pytensor-3.0.1 → pytensor-3.1.0}/doc/extending/unittest.rst +0 -0
  125. {pytensor-3.0.1 → pytensor-3.1.0}/doc/extending/using_params.rst +0 -0
  126. {pytensor-3.0.1 → pytensor-3.1.0}/doc/faq.rst +0 -0
  127. {pytensor-3.0.1 → pytensor-3.1.0}/doc/gallery/applications/normalizing_flows_in_pytensor.ipynb +0 -0
  128. {pytensor-3.0.1 → pytensor-3.1.0}/doc/gallery/autodiff/vector_jacobian_product.ipynb +0 -0
  129. {pytensor-3.0.1 → pytensor-3.1.0}/doc/gallery/introduction/pytensor_intro.ipynb +0 -0
  130. {pytensor-3.0.1 → pytensor-3.1.0}/doc/gallery/optimize/root.ipynb +0 -0
  131. {pytensor-3.0.1 → pytensor-3.1.0}/doc/gallery/page_footer.md +0 -0
  132. {pytensor-3.0.1 → pytensor-3.1.0}/doc/gallery/rewrites/graph_rewrites.ipynb +0 -0
  133. {pytensor-3.0.1 → pytensor-3.1.0}/doc/gallery/scan/scan_tutorial.ipynb +0 -0
  134. {pytensor-3.0.1 → pytensor-3.1.0}/doc/generate_dtype_tensor_table.py +0 -0
  135. {pytensor-3.0.1 → pytensor-3.1.0}/doc/glossary.rst +0 -0
  136. {pytensor-3.0.1 → pytensor-3.1.0}/doc/images/Elman_srnn.png +0 -0
  137. {pytensor-3.0.1 → pytensor-3.1.0}/doc/images/PyTensor.png +0 -0
  138. {pytensor-3.0.1 → pytensor-3.1.0}/doc/images/PyTensor_RGB.svg +0 -0
  139. {pytensor-3.0.1 → pytensor-3.1.0}/doc/images/PyTensor_logo.png +0 -0
  140. {pytensor-3.0.1 → pytensor-3.1.0}/doc/images/binder.svg +0 -0
  141. {pytensor-3.0.1 → pytensor-3.1.0}/doc/images/blocksparse.png +0 -0
  142. {pytensor-3.0.1 → pytensor-3.1.0}/doc/images/colab.svg +0 -0
  143. {pytensor-3.0.1 → pytensor-3.1.0}/doc/images/github.svg +0 -0
  144. {pytensor-3.0.1 → pytensor-3.1.0}/doc/images/lstm.png +0 -0
  145. {pytensor-3.0.1 → pytensor-3.1.0}/doc/images/lstm_memorycell.png +0 -0
  146. {pytensor-3.0.1 → pytensor-3.1.0}/doc/images/talk2010.gif +0 -0
  147. {pytensor-3.0.1 → pytensor-3.1.0}/doc/images/talk2010.png +0 -0
  148. {pytensor-3.0.1 → pytensor-3.1.0}/doc/index.rst +0 -0
  149. {pytensor-3.0.1 → pytensor-3.1.0}/doc/install.rst +0 -0
  150. {pytensor-3.0.1 → pytensor-3.1.0}/doc/internal/how_to_release.rst +0 -0
  151. {pytensor-3.0.1 → pytensor-3.1.0}/doc/internal/index.rst +0 -0
  152. {pytensor-3.0.1 → pytensor-3.1.0}/doc/internal/metadocumentation.rst +0 -0
  153. {pytensor-3.0.1 → pytensor-3.1.0}/doc/introduction.rst +0 -0
  154. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/compile/debugmode.rst +0 -0
  155. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/compile/function.rst +0 -0
  156. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/compile/index.rst +0 -0
  157. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/compile/io.rst +0 -0
  158. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/compile/mode.rst +0 -0
  159. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/compile/nanguardmode.rst +0 -0
  160. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/compile/opfromgraph.rst +0 -0
  161. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/compile/ops.rst +0 -0
  162. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/compile/profilemode.rst +0 -0
  163. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/compile/shared.rst +0 -0
  164. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/config.rst +0 -0
  165. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/d3viz/examples/d3viz/css/d3-context-menu.css +0 -0
  166. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/d3viz/examples/d3viz/css/d3viz.css +0 -0
  167. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/d3viz/examples/d3viz/js/d3-context-menu.js +0 -0
  168. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/d3viz/examples/d3viz/js/d3.v3.min.js +0 -0
  169. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/d3viz/examples/d3viz/js/d3viz.js +0 -0
  170. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/d3viz/examples/d3viz/js/dagre-d3.min.js +0 -0
  171. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/d3viz/examples/d3viz/js/graphlib-dot.min.js +0 -0
  172. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/d3viz/examples/mlp.html +0 -0
  173. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/d3viz/examples/mlp.png +0 -0
  174. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/d3viz/examples/mlp2.html +0 -0
  175. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/d3viz/examples/mlp2.pdf +0 -0
  176. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/d3viz/examples/mlp2.png +0 -0
  177. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/d3viz/examples/ofg.html +0 -0
  178. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/d3viz/examples/ofg2.html +0 -0
  179. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/d3viz/index.ipynb +0 -0
  180. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/d3viz/index.rst +0 -0
  181. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/d3viz/index_files/index_10_0.png +0 -0
  182. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/d3viz/index_files/index_11_0.png +0 -0
  183. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/d3viz/index_files/index_24_0.png +0 -0
  184. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/d3viz/index_files/index_25_0.png +0 -0
  185. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/graph/features.rst +0 -0
  186. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/graph/fgraph.rst +0 -0
  187. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/graph/graph.rst +0 -0
  188. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/graph/index.rst +0 -0
  189. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/graph/op.rst +0 -0
  190. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/graph/replace.rst +0 -0
  191. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/graph/type.rst +0 -0
  192. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/graph/utils.rst +0 -0
  193. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/index.rst +0 -0
  194. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/misc/pkl_utils.rst +0 -0
  195. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/printing.rst +0 -0
  196. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/scalar/index.rst +0 -0
  197. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/scan.rst +0 -0
  198. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/sparse/index.rst +0 -0
  199. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/sparse/sandbox.rst +0 -0
  200. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/tensor/basic.rst +0 -0
  201. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/tensor/basic_opt.rst +0 -0
  202. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/tensor/bcast.png +0 -0
  203. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/tensor/bcast.svg +0 -0
  204. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/tensor/elemwise.rst +0 -0
  205. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/tensor/extra_ops.rst +0 -0
  206. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/tensor/fft.rst +0 -0
  207. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/tensor/functional.rst +0 -0
  208. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/tensor/index.rst +0 -0
  209. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/tensor/io.rst +0 -0
  210. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/tensor/linalg.rst +0 -0
  211. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/tensor/math_opt.rst +0 -0
  212. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/tensor/nlinalg.rst +0 -0
  213. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/tensor/optimize.rst +0 -0
  214. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/tensor/plot_fft.png +0 -0
  215. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/tensor/random.rst +0 -0
  216. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/tensor/slinalg.rst +0 -0
  217. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/tensor/utils.rst +0 -0
  218. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/typed_list.rst +0 -0
  219. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/xtensor/index.md +0 -0
  220. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/xtensor/linalg.md +0 -0
  221. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/xtensor/math.md +0 -0
  222. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/xtensor/module_functions.md +0 -0
  223. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/xtensor/random.md +0 -0
  224. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/xtensor/signal.md +0 -0
  225. {pytensor-3.0.1 → pytensor-3.1.0}/doc/library/xtensor/type.md +0 -0
  226. {pytensor-3.0.1 → pytensor-3.1.0}/doc/links.rst +0 -0
  227. {pytensor-3.0.1 → pytensor-3.1.0}/doc/optimizations.rst +0 -0
  228. {pytensor-3.0.1 → pytensor-3.1.0}/doc/pylintrc +0 -0
  229. {pytensor-3.0.1 → pytensor-3.1.0}/doc/robots.txt +0 -0
  230. {pytensor-3.0.1 → pytensor-3.1.0}/doc/troubleshooting.rst +0 -0
  231. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/adding.rst +0 -0
  232. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/adding_solution_1.py +0 -0
  233. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/aliasing.rst +0 -0
  234. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/apply.png +0 -0
  235. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/apply.svg +0 -0
  236. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/bcast.png +0 -0
  237. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/broadcasting.rst +0 -0
  238. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/conditions.rst +0 -0
  239. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/debug_faq.rst +0 -0
  240. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/dlogistic.png +0 -0
  241. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/examples.rst +0 -0
  242. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/faq_tutorial.rst +0 -0
  243. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/gradients.rst +0 -0
  244. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/index.rst +0 -0
  245. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/loading_and_saving.rst +0 -0
  246. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/logistic.gp +0 -0
  247. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/logistic.png +0 -0
  248. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/loop.rst +0 -0
  249. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/loop_solution_1.py +0 -0
  250. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/modes.rst +0 -0
  251. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/modes_solution_1.py +0 -0
  252. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/multi_cores.rst +0 -0
  253. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/nan_tutorial.rst +0 -0
  254. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/pics/d3viz.png +0 -0
  255. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/pics/logreg_pydotprint_predict.png +0 -0
  256. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/pics/logreg_pydotprint_prediction.png +0 -0
  257. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/pics/logreg_pydotprint_train.png +0 -0
  258. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/printing_drawing.rst +0 -0
  259. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/prng.rst +0 -0
  260. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/profiling.rst +0 -0
  261. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/profiling_example.py +0 -0
  262. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/profiling_example_out.prof +0 -0
  263. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/shape_info.rst +0 -0
  264. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/sparse.rst +0 -0
  265. {pytensor-3.0.1 → pytensor-3.1.0}/doc/tutorial/symbolic_graphs.rst +0 -0
  266. {pytensor-3.0.1 → pytensor-3.1.0}/doc/user_guide.rst +0 -0
  267. {pytensor-3.0.1 → pytensor-3.1.0}/pyproject.toml +0 -0
  268. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/__init__.py +0 -0
  269. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/_sparse_lazy.py +0 -0
  270. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/basic.py +0 -0
  271. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/bin/__init__.py +0 -0
  272. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/bin/pytensor_cache.py +0 -0
  273. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/breakpoint.py +0 -0
  274. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/compile/__init__.py +0 -0
  275. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/compile/aliasing.py +0 -0
  276. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/compile/builders.py +0 -0
  277. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/compile/compiledir.py +0 -0
  278. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/compile/compilelock.py +0 -0
  279. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/compile/debug/__init__.py +0 -0
  280. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/compile/debug/debugmode.py +0 -0
  281. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/compile/debug/dump.py +0 -0
  282. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/compile/debug/monitormode.py +0 -0
  283. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/compile/debug/nanguardmode.py +0 -0
  284. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/compile/debug/profiling.py +0 -0
  285. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/compile/io.py +0 -0
  286. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/compile/mode.py +0 -0
  287. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/compile/ops.py +0 -0
  288. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/compile/rebuild.py +0 -0
  289. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/compile/sharedvalue.py +0 -0
  290. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/configparser.py +0 -0
  291. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/d3viz/__init__.py +0 -0
  292. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/d3viz/css/d3-context-menu.css +0 -0
  293. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/d3viz/css/d3viz.css +0 -0
  294. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/d3viz/d3viz.py +0 -0
  295. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/d3viz/formatting.py +0 -0
  296. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/d3viz/html/template.html +0 -0
  297. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/d3viz/js/d3-context-menu.js +0 -0
  298. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/d3viz/js/d3.v3.min.js +0 -0
  299. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/d3viz/js/d3viz.js +0 -0
  300. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/d3viz/js/dagre-d3.min.js +0 -0
  301. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/d3viz/js/graphlib-dot.min.js +0 -0
  302. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/gradient.py +0 -0
  303. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/graph/__init__.py +0 -0
  304. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/graph/destroyhandler.py +0 -0
  305. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/graph/features.py +0 -0
  306. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/graph/null_type.py +0 -0
  307. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/graph/op.py +0 -0
  308. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/graph/replace.py +0 -0
  309. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/graph/rewriting/__init__.py +0 -0
  310. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/graph/rewriting/basic.py +0 -0
  311. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/graph/rewriting/db.py +0 -0
  312. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/graph/rewriting/kanren.py +0 -0
  313. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/graph/rewriting/unify.py +0 -0
  314. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/graph/traversal.py +0 -0
  315. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/graph/type.py +0 -0
  316. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/graph/utils.py +0 -0
  317. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/ifelse.py +0 -0
  318. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/ipython.py +0 -0
  319. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/__init__.py +0 -0
  320. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/basic.py +0 -0
  321. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/c/__init__.py +0 -0
  322. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/c/basic.py +0 -0
  323. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/c/c_code/lazylinker_c.c +0 -0
  324. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/c/c_code/pytensor_mod_helper.h +0 -0
  325. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/c/cmodule.py +0 -0
  326. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/c/cutils.py +0 -0
  327. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/c/cvm.py +0 -0
  328. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/c/exceptions.py +0 -0
  329. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/c/interface.py +0 -0
  330. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/c/lazylinker_c.py +0 -0
  331. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/c/op.py +0 -0
  332. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/c/params_type.py +0 -0
  333. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/c/type.py +0 -0
  334. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/jax/__init__.py +0 -0
  335. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/jax/dispatch/__init__.py +0 -0
  336. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/jax/dispatch/basic.py +0 -0
  337. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/jax/dispatch/blas.py +0 -0
  338. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/jax/dispatch/blockwise.py +0 -0
  339. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/jax/dispatch/einsum.py +0 -0
  340. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/jax/dispatch/elemwise.py +0 -0
  341. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/jax/dispatch/extra_ops.py +0 -0
  342. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/jax/dispatch/linalg/__init__.py +0 -0
  343. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/jax/dispatch/linalg/constructors.py +0 -0
  344. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/jax/dispatch/linalg/decomposition.py +0 -0
  345. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/jax/dispatch/linalg/inverse.py +0 -0
  346. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/jax/dispatch/linalg/products.py +0 -0
  347. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/jax/dispatch/linalg/solvers.py +0 -0
  348. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/jax/dispatch/linalg/summary.py +0 -0
  349. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/jax/dispatch/math.py +0 -0
  350. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/jax/dispatch/pad.py +0 -0
  351. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/jax/dispatch/random.py +0 -0
  352. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/jax/dispatch/scalar.py +0 -0
  353. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/jax/dispatch/scan.py +0 -0
  354. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/jax/dispatch/shape.py +0 -0
  355. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/jax/dispatch/signal/__init__.py +0 -0
  356. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/jax/dispatch/signal/conv.py +0 -0
  357. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/jax/dispatch/sort.py +0 -0
  358. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/jax/dispatch/sparse.py +0 -0
  359. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/jax/dispatch/subtensor.py +0 -0
  360. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/jax/dispatch/tensor_basic.py +0 -0
  361. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/jax/ops.py +0 -0
  362. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/mlx/__init__.py +0 -0
  363. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/mlx/dispatch/__init__.py +0 -0
  364. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/mlx/dispatch/basic.py +0 -0
  365. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/mlx/dispatch/blas.py +0 -0
  366. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/mlx/dispatch/blockwise.py +0 -0
  367. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/mlx/dispatch/einsum.py +0 -0
  368. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/mlx/dispatch/elemwise.py +0 -0
  369. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/mlx/dispatch/extra_ops.py +0 -0
  370. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/mlx/dispatch/linalg/__init__.py +0 -0
  371. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/mlx/dispatch/linalg/decomposition.py +0 -0
  372. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/mlx/dispatch/linalg/inverse.py +0 -0
  373. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/mlx/dispatch/linalg/products.py +0 -0
  374. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/mlx/dispatch/linalg/solvers.py +0 -0
  375. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/mlx/dispatch/linalg/summary.py +0 -0
  376. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/mlx/dispatch/math.py +0 -0
  377. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/mlx/dispatch/pad.py +0 -0
  378. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/mlx/dispatch/scalar.py +0 -0
  379. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/mlx/dispatch/shape.py +0 -0
  380. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/mlx/dispatch/signal/__init__.py +0 -0
  381. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/mlx/dispatch/signal/conv.py +0 -0
  382. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/mlx/dispatch/sort.py +0 -0
  383. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/mlx/dispatch/subtensor.py +0 -0
  384. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/mlx/dispatch/tensor_basic.py +0 -0
  385. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/__init__.py +0 -0
  386. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/cache.py +0 -0
  387. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/__init__.py +0 -0
  388. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/blockwise.py +0 -0
  389. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/compile_ops.py +0 -0
  390. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/cython_support.py +0 -0
  391. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/extra_ops.py +0 -0
  392. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/linalg/constructors.py +0 -0
  393. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/linalg/decomposition/__init__.py +0 -0
  394. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/linalg/decomposition/cholesky.py +0 -0
  395. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/linalg/decomposition/eigen.py +0 -0
  396. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/linalg/decomposition/lu.py +0 -0
  397. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/linalg/decomposition/lu_factor.py +0 -0
  398. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/linalg/decomposition/qr.py +0 -0
  399. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/linalg/decomposition/qz.py +0 -0
  400. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/linalg/decomposition/schur.py +0 -0
  401. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/linalg/inverse.py +0 -0
  402. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/linalg/solvers/__init__.py +0 -0
  403. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/linalg/solvers/cholesky.py +0 -0
  404. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/linalg/solvers/general.py +0 -0
  405. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/linalg/solvers/hermitian.py +0 -0
  406. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/linalg/solvers/linear_control.py +0 -0
  407. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/linalg/solvers/lu_solve.py +0 -0
  408. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/linalg/solvers/posdef.py +0 -0
  409. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/linalg/solvers/symmetric.py +0 -0
  410. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/linalg/solvers/triangular.py +0 -0
  411. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/linalg/solvers/tridiagonal.py +0 -0
  412. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/linalg/solvers/utils.py +0 -0
  413. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/linalg/summary.py +0 -0
  414. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/linalg/utils.py +0 -0
  415. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/random.py +0 -0
  416. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/scalar.py +0 -0
  417. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/shape.py +0 -0
  418. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/signal/__init__.py +0 -0
  419. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/signal/conv.py +0 -0
  420. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/sort.py +0 -0
  421. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/sparse/__init__.py +0 -0
  422. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/sparse/basic.py +0 -0
  423. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/sparse/math.py +0 -0
  424. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/sparse/variable.py +0 -0
  425. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/string_codegen.py +0 -0
  426. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/subtensor.py +0 -0
  427. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/tensor_basic.py +0 -0
  428. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/typed_list.py +0 -0
  429. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/numba/dispatch/vectorize_codegen.py +0 -0
  430. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/pytorch/dispatch/__init__.py +0 -0
  431. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/pytorch/dispatch/basic.py +0 -0
  432. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/pytorch/dispatch/blas.py +0 -0
  433. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/pytorch/dispatch/blockwise.py +0 -0
  434. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/pytorch/dispatch/elemwise.py +0 -0
  435. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/pytorch/dispatch/extra_ops.py +0 -0
  436. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/pytorch/dispatch/linalg/__init__.py +0 -0
  437. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/pytorch/dispatch/linalg/decomposition.py +0 -0
  438. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/pytorch/dispatch/linalg/inverse.py +0 -0
  439. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/pytorch/dispatch/linalg/products.py +0 -0
  440. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/pytorch/dispatch/linalg/summary.py +0 -0
  441. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/pytorch/dispatch/math.py +0 -0
  442. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/pytorch/dispatch/scalar.py +0 -0
  443. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/pytorch/dispatch/shape.py +0 -0
  444. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/pytorch/dispatch/sort.py +0 -0
  445. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/pytorch/dispatch/subtensor.py +0 -0
  446. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/link/vm.py +0 -0
  447. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/misc/__init__.py +0 -0
  448. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/misc/check_blas.py +0 -0
  449. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/misc/check_blas_many.sh +0 -0
  450. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/misc/check_duplicate_key.py +0 -0
  451. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/misc/elemwise_openmp_speedup.py +0 -0
  452. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/misc/elemwise_time_test.py +0 -0
  453. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/misc/frozendict.py +0 -0
  454. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/misc/may_share_memory.py +0 -0
  455. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/misc/ordered_set.py +0 -0
  456. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/misc/pkl_utils.py +0 -0
  457. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/npy_2_compat.py +0 -0
  458. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/py.typed +0 -0
  459. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/raise_op.py +0 -0
  460. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/scalar/__init__.py +0 -0
  461. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/scalar/c_code/Faddeeva.cc +0 -0
  462. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/scalar/c_code/Faddeeva.hh +0 -0
  463. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/scalar/c_code/gamma.c +0 -0
  464. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/scalar/c_code/incbet.c +0 -0
  465. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/scalar/math.py +0 -0
  466. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/scalar/sharedvar.py +0 -0
  467. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/scan/__init__.py +0 -0
  468. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/scan/basic.py +0 -0
  469. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/scan/checkpoints.py +0 -0
  470. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/scan/scan_perform.pyx +0 -0
  471. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/scan/scan_perform_ext.py +0 -0
  472. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/scan/utils.py +0 -0
  473. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/scan/views.py +0 -0
  474. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/sparse/__init__.py +0 -0
  475. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/sparse/linalg.py +0 -0
  476. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/sparse/math.py +0 -0
  477. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/sparse/rewriting.py +0 -0
  478. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/sparse/sharedvar.py +0 -0
  479. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/sparse/type.py +0 -0
  480. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/sparse/utils.py +0 -0
  481. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/sparse/variable.py +0 -0
  482. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/__init__.py +0 -0
  483. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/blas.py +0 -0
  484. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/blas_c.py +0 -0
  485. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/blas_headers.py +0 -0
  486. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/blockwise.py +0 -0
  487. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/c_code/alt_blas_common.h +0 -0
  488. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/c_code/alt_blas_template.c +0 -0
  489. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/c_code/dimshuffle.c +0 -0
  490. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/einsum.py +0 -0
  491. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/elemwise.py +0 -0
  492. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/elemwise_cgen.py +0 -0
  493. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/exceptions.py +0 -0
  494. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/fft.py +0 -0
  495. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/fourier.py +0 -0
  496. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/functional.py +0 -0
  497. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/interpolate.py +0 -0
  498. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/linalg/__init__.py +0 -0
  499. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/linalg/_lazy.py +0 -0
  500. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/linalg/constructors.py +0 -0
  501. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/linalg/decomposition/__init__.py +0 -0
  502. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/linalg/decomposition/cholesky.py +0 -0
  503. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/linalg/decomposition/eigen.py +0 -0
  504. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/linalg/decomposition/lu.py +0 -0
  505. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/linalg/decomposition/qr.py +0 -0
  506. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/linalg/decomposition/schur.py +0 -0
  507. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/linalg/dtype_utils.py +0 -0
  508. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/linalg/inverse.py +0 -0
  509. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/linalg/solvers/__init__.py +0 -0
  510. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/linalg/solvers/core.py +0 -0
  511. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/linalg/solvers/general.py +0 -0
  512. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/linalg/solvers/linear_control.py +0 -0
  513. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/linalg/solvers/lstsq.py +0 -0
  514. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/linalg/solvers/psd.py +0 -0
  515. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/linalg/solvers/triangular.py +0 -0
  516. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/linalg/solvers/tridiagonal.py +0 -0
  517. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/linalg/summary.py +0 -0
  518. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/math.py +0 -0
  519. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/nlinalg.py +0 -0
  520. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/optimize.py +0 -0
  521. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/pad.py +0 -0
  522. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/random/__init__.py +0 -0
  523. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/random/basic.py +0 -0
  524. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/random/op.py +0 -0
  525. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/random/rewriting/__init__.py +0 -0
  526. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/random/rewriting/basic.py +0 -0
  527. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/random/rewriting/jax.py +0 -0
  528. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/random/rewriting/numba.py +0 -0
  529. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/random/type.py +0 -0
  530. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/random/utils.py +0 -0
  531. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/random/variable.py +0 -0
  532. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/reshape.py +0 -0
  533. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/rewriting/blas.py +0 -0
  534. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/rewriting/blas_c.py +0 -0
  535. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/rewriting/blockwise.py +0 -0
  536. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/rewriting/einsum.py +0 -0
  537. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/rewriting/extra_ops.py +0 -0
  538. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/rewriting/jax.py +0 -0
  539. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/rewriting/linalg/__init__.py +0 -0
  540. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/rewriting/numba.py +0 -0
  541. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/rewriting/optimize.py +0 -0
  542. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/rewriting/reshape.py +0 -0
  543. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/rewriting/shape.py +0 -0
  544. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/rewriting/special.py +0 -0
  545. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/rewriting/uncanonicalize.py +0 -0
  546. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/shape.py +0 -0
  547. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/sharedvar.py +0 -0
  548. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/signal/__init__.py +0 -0
  549. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/signal/conv.py +0 -0
  550. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/slinalg.py +0 -0
  551. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/sort.py +0 -0
  552. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/symbolic.py +0 -0
  553. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/type.py +0 -0
  554. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/type_other.py +0 -0
  555. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/utils.py +0 -0
  556. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/var.py +0 -0
  557. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/tensor/xlogx.py +0 -0
  558. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/typed_list/__init__.py +0 -0
  559. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/typed_list/basic.py +0 -0
  560. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/typed_list/rewriting.py +0 -0
  561. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/typed_list/type.py +0 -0
  562. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/utils.py +0 -0
  563. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/xtensor/__init__.py +0 -0
  564. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/xtensor/indexing.py +0 -0
  565. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/xtensor/math.py +0 -0
  566. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/xtensor/random/__init__.py +0 -0
  567. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/xtensor/random/basic.py +0 -0
  568. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/xtensor/random/type.py +0 -0
  569. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/xtensor/random/variable.py +0 -0
  570. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/xtensor/reduction.py +0 -0
  571. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/xtensor/rewriting/__init__.py +0 -0
  572. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/xtensor/rewriting/basic.py +0 -0
  573. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/xtensor/rewriting/indexing.py +0 -0
  574. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/xtensor/rewriting/math.py +0 -0
  575. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/xtensor/rewriting/reduction.py +0 -0
  576. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/xtensor/rewriting/shape.py +0 -0
  577. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/xtensor/rewriting/utils.py +0 -0
  578. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/xtensor/rewriting/vectorization.py +0 -0
  579. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/xtensor/shape.py +0 -0
  580. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/xtensor/signal.py +0 -0
  581. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor/xtensor/vectorization.py +0 -0
  582. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor.egg-info/dependency_links.txt +0 -0
  583. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor.egg-info/entry_points.txt +0 -0
  584. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor.egg-info/requires.txt +0 -0
  585. {pytensor-3.0.1 → pytensor-3.1.0}/pytensor.egg-info/top_level.txt +0 -0
  586. {pytensor-3.0.1 → pytensor-3.1.0}/scripts/slowest_tests/update-slowest-times-issue.sh +0 -0
  587. {pytensor-3.0.1 → pytensor-3.1.0}/setup.cfg +0 -0
  588. {pytensor-3.0.1 → pytensor-3.1.0}/setup.py +0 -0
  589. {pytensor-3.0.1 → pytensor-3.1.0}/tests/link/c/c_code/test_cenum.h +0 -0
  590. {pytensor-3.0.1 → pytensor-3.1.0}/tests/link/c/c_code/test_quadratic_function.c +0 -0
  591. {pytensor-3.0.1 → pytensor-3.1.0}/tests/test_basic.py +0 -0
  592. {pytensor-3.0.1 → pytensor-3.1.0}/tests/test_breakpoint.py +0 -0
  593. {pytensor-3.0.1 → pytensor-3.1.0}/tests/test_config.py +0 -0
  594. {pytensor-3.0.1 → pytensor-3.1.0}/tests/test_gradient.py +0 -0
  595. {pytensor-3.0.1 → pytensor-3.1.0}/tests/test_ifelse.py +0 -0
  596. {pytensor-3.0.1 → pytensor-3.1.0}/tests/test_raise_op.py +0 -0
  597. {pytensor-3.0.1 → pytensor-3.1.0}/tests/test_rop.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytensor
3
- Version: 3.0.1
3
+ Version: 3.1.0
4
4
  Summary: Optimizing compiler for evaluating mathematical expressions on CPUs and GPUs.
5
5
  Author-email: pymc-devs <pymc.devs@gmail.com>
6
6
  License-Expression: BSD-3-Clause
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2026-05-07T19:28:03+0200",
11
+ "date": "2026-05-20T17:31:39-0500",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "10852419d00cf04f0c89c034f04ddfa9e5067e63",
15
- "version": "3.0.1"
14
+ "full-revisionid": "b712ea6e6a7541ce24c47f8869699178958dadf6",
15
+ "version": "3.1.0"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -0,0 +1,88 @@
1
+ # Import every rule module so its registrations land on the global registry that
2
+ # ``check_assumption`` consults. The property modules pull in the shared helper
3
+ # modules (alloc, dimshuffle, subtensor, dot, elemwise) transitively.
4
+ import pytensor.assumptions.alloc
5
+ import pytensor.assumptions.blockwise
6
+ import pytensor.assumptions.diagonal
7
+ import pytensor.assumptions.dimshuffle
8
+ import pytensor.assumptions.orthogonal
9
+ import pytensor.assumptions.positive_definite
10
+ import pytensor.assumptions.reshape
11
+ import pytensor.assumptions.shape
12
+ import pytensor.assumptions.subtensor
13
+ import pytensor.assumptions.symmetric
14
+ import pytensor.assumptions.triangular
15
+ from pytensor.assumptions.core import (
16
+ ALL_KEYS,
17
+ DIAGONAL,
18
+ IMPLIES,
19
+ LOWER_TRIANGULAR,
20
+ ORTHOGONAL,
21
+ POSITIVE_DEFINITE,
22
+ SYMMETRIC,
23
+ UPPER_TRIANGULAR,
24
+ AssumptionFeature,
25
+ AssumptionKey,
26
+ ConflictingAssumptionsError,
27
+ FactState,
28
+ check_assumption,
29
+ register_assumption,
30
+ register_constant_inference,
31
+ register_implies,
32
+ )
33
+ from pytensor.assumptions.specify import (
34
+ SpecifyAssumptions,
35
+ assume,
36
+ specify_assumption_rule,
37
+ )
38
+ from pytensor.graph.fg import FunctionGraph
39
+
40
+
41
+ def summarize_assumptions(feature: AssumptionFeature, var) -> str:
42
+ """Return a compact ``{...}`` tag of the facts known about *var*.
43
+
44
+ Each TRUE fact appears as its ``short_name``; each FALSE fact as ``!short_name``.
45
+ Implied facts are pruned: a TRUE fact is dropped when a stronger fact (one that
46
+ implies it) is also TRUE, and a FALSE fact is dropped when a weaker fact (one it
47
+ implies) is also FALSE. Return ``""`` when nothing is known.
48
+ """
49
+ states = {}
50
+ for key in ALL_KEYS:
51
+ try:
52
+ states[key] = feature.get(var, key)
53
+ except ConflictingAssumptionsError:
54
+ states[key] = FactState.CONFLICT
55
+
56
+ tokens = []
57
+ for key in ALL_KEYS:
58
+ state = states[key]
59
+ label = key.short_name or key.name
60
+ if state is FactState.TRUE:
61
+ if any(
62
+ key in weaker and states[stronger] is FactState.TRUE
63
+ for stronger, weaker in IMPLIES.items()
64
+ ):
65
+ continue
66
+ tokens.append(label)
67
+ elif state is FactState.FALSE:
68
+ if any(states[w] is FactState.FALSE for w in IMPLIES.get(key, ())):
69
+ continue
70
+ tokens.append(f"!{label}")
71
+ elif state is FactState.CONFLICT:
72
+ tokens.append(f"{label}=CONFLICT")
73
+
74
+ return "{" + ", ".join(tokens) + "}" if tokens else ""
75
+
76
+
77
+ def assumption_tags(outputs) -> dict:
78
+ """Map each variable reachable from *outputs* to its :func:`summarize_assumptions`
79
+ tag, omitting variables with no known facts."""
80
+ fgraph = FunctionGraph(outputs=list(outputs), clone=False)
81
+ feature = AssumptionFeature()
82
+ fgraph.attach_feature(feature)
83
+ tags = {}
84
+ for var in fgraph.variables:
85
+ tag = summarize_assumptions(feature, var)
86
+ if tag:
87
+ tags[var] = tag
88
+ return tags
@@ -0,0 +1,90 @@
1
+ from pytensor.assumptions.core import (
2
+ ALL_KEYS,
3
+ FactState,
4
+ register_assumption,
5
+ true_if,
6
+ )
7
+ from pytensor.tensor.basic import (
8
+ Alloc,
9
+ NotScalarConstantError,
10
+ get_underlying_scalar_constant_value,
11
+ )
12
+ from pytensor.tensor.variable import TensorConstant
13
+
14
+
15
+ def alloc_of_zero(key, op, feature, fgraph, node, input_states) -> list[FactState]:
16
+ """``Alloc`` rule for DIAGONAL / LOWER_TRIANGULAR / UPPER_TRIANGULAR: TRUE when
17
+ the fill value is the scalar 0 (an all-zero square matrix), FALSE when it is a
18
+ known non-zero scalar -- the off-diagonal entries are then non-zero, so none of
19
+ these properties holds.
20
+
21
+ Requires the trailing two output dims to be statically known and equal; these
22
+ properties apply only to square matrices. SYMMETRIC uses :func:`alloc_is_symmetric`
23
+ instead, since a constant fill is symmetric whatever its value.
24
+ """
25
+ out_shape = node.outputs[0].type.shape
26
+ if len(out_shape) < 2:
27
+ return [FactState.UNKNOWN]
28
+ m, n = out_shape[-2], out_shape[-1]
29
+ if m is None or n is None or m != n:
30
+ return [FactState.UNKNOWN]
31
+ try:
32
+ val = get_underlying_scalar_constant_value(node.inputs[0])
33
+ except NotScalarConstantError:
34
+ return [FactState.UNKNOWN]
35
+ return true_if(val == 0, else_false=True)
36
+
37
+
38
+ def alloc_is_symmetric(key, op, feature, fgraph, node, input_states) -> list[FactState]:
39
+ """``Alloc`` rule for SYMMETRIC: a scalar fill broadcasts one value to every
40
+ entry, so a square output equals its transpose -- whatever the value.
41
+
42
+ A non-scalar fill (e.g. a vector broadcast across rows) is left UNKNOWN.
43
+ """
44
+ out_shape = node.outputs[0].type.shape
45
+ if len(out_shape) < 2:
46
+ return [FactState.UNKNOWN]
47
+ m, n = out_shape[-2], out_shape[-1]
48
+ if m is None or n is None or m != n:
49
+ return [FactState.UNKNOWN]
50
+ return true_if(node.inputs[0].type.ndim == 0)
51
+
52
+
53
+ def eye_identity_rule(key, op, feature, fgraph, node, input_states) -> list[FactState]:
54
+ """Rule body: TRUE when an :class:`Eye` node produces the identity matrix (square, k == 0)."""
55
+ n, m, k = node.inputs
56
+ if not isinstance(k, TensorConstant):
57
+ return [FactState.UNKNOWN]
58
+ if k.data.item() != 0:
59
+ # Ones sit on an off-main diagonal, so this is not the identity.
60
+ return [FactState.FALSE]
61
+ if n is m:
62
+ return [FactState.TRUE]
63
+ if isinstance(n, TensorConstant) and isinstance(m, TensorConstant):
64
+ return true_if(n.data.item() == m.data.item(), else_false=True)
65
+ return [FactState.UNKNOWN]
66
+
67
+
68
+ def alloc_diag_at_offset_zero(
69
+ key, op, feature, fgraph, node, input_states
70
+ ) -> list[FactState]:
71
+ """Rule body: TRUE when :class:`AllocDiag` places values on the main diagonal,
72
+ FALSE when it places them on any other diagonal (off-main entries break diagonal /
73
+ symmetric / PD structure regardless of the diagonal vector's values)."""
74
+ return [FactState.TRUE if op.offset == 0 else FactState.FALSE]
75
+
76
+
77
+ def alloc_propagates_matrix_property(
78
+ key, op, feature, fgraph, node, input_states
79
+ ) -> list[FactState]:
80
+ """``Alloc`` broadcasts a value across new or expanded leading axes. When the
81
+ value is already matrix-shaped with non-broadcastable core axes, the alloc
82
+ cannot touch those axes, so the matrix property carries through unchanged."""
83
+ value = node.inputs[0]
84
+ if value.type.ndim >= 2 and not any(value.type.broadcastable[-2:]):
85
+ return [input_states[0]]
86
+ return [FactState.UNKNOWN]
87
+
88
+
89
+ for _key in ALL_KEYS:
90
+ register_assumption(_key, Alloc)(alloc_propagates_matrix_property)
@@ -0,0 +1,17 @@
1
+ from pytensor.assumptions.core import (
2
+ ALL_KEYS,
3
+ infer_assumption_for_node,
4
+ register_assumption,
5
+ )
6
+ from pytensor.tensor.blockwise import Blockwise
7
+
8
+
9
+ def _blockwise_delegate(key, op, feature, fgraph, node, input_states):
10
+ """Delegate assumption inference to the ``core_op`` of a Blockwise wrapper."""
11
+ return infer_assumption_for_node(
12
+ key, op.core_op, feature, fgraph, node, input_states
13
+ )
14
+
15
+
16
+ for _key in ALL_KEYS:
17
+ register_assumption(_key, Blockwise)(_blockwise_delegate)
@@ -0,0 +1,373 @@
1
+ from collections import deque
2
+ from collections.abc import Callable
3
+ from dataclasses import dataclass
4
+ from enum import IntFlag, auto
5
+ from typing import Any
6
+
7
+ from pytensor.graph import Apply, FunctionGraph, Op
8
+ from pytensor.graph.features import AlreadyThere, Feature
9
+ from pytensor.graph.traversal import walk_toposort
10
+ from pytensor.tensor.variable import TensorConstant
11
+
12
+
13
+ class FactState(IntFlag):
14
+ """Three-valued logic for assumption inference.
15
+
16
+ The three fact states are TRUE, FALSE and UNKNOWN.
17
+
18
+ UNKNOWN is the default condition, in which we cannot confirm or deny the fact. TRUE and FALSE are definitive
19
+ states. If we have evidence that a fact is both TRUE and FALSE, we get CONFLICT. In general, a CONFLICT state
20
+ should not be possible.
21
+ """
22
+
23
+ UNKNOWN = 0
24
+ TRUE = auto()
25
+ FALSE = auto()
26
+ CONFLICT = TRUE | FALSE
27
+
28
+ def __bool__(self) -> bool:
29
+ raise TypeError(
30
+ "FactState has no boolean value; compare against FactState.TRUE, "
31
+ "FactState.UNKNOWN, etc. explicitly, or use AssumptionFeature.check()."
32
+ )
33
+
34
+ @classmethod
35
+ def join(cls, left: "FactState", right: "FactState") -> "FactState":
36
+ """Combine two pieces of evidence about the *same* (variable, key)."""
37
+ return cls(left | right)
38
+
39
+
40
+ @dataclass(frozen=True)
41
+ class AssumptionKey:
42
+ """Identifies a named structural property (e.g. "diagonal" or "triangular").
43
+
44
+ ``short_name`` is an abbreviated label used by ``debugprint(print_assumptions=True)``;
45
+ it falls back to ``name`` when empty.
46
+ """
47
+
48
+ name: str
49
+ short_name: str = ""
50
+
51
+ def __repr__(self) -> str:
52
+ return self.name
53
+
54
+
55
+ class ConflictingAssumptionsError(ValueError):
56
+ """Raised when joining evidence about a (variable, key) produces ``FactState.CONFLICT``.
57
+
58
+ Surfaces contradictions between user-asserted facts and inferred facts (or between
59
+ multiple sources of inferred facts) before they can silently corrupt downstream rewrites.
60
+ """
61
+
62
+
63
+ # An inference function takes the active AssumptionKey, the Op, the current AssumptionFeature, the FunctionGraph,
64
+ # the Apply node being analyzed, and the states of the input variables for the current key. It returns a list of
65
+ # FactState with one entry per node output.
66
+ InferFactFn = Callable[
67
+ [AssumptionKey, Op, "AssumptionFeature", FunctionGraph, Apply, list[FactState]],
68
+ list[FactState],
69
+ ]
70
+
71
+ # The global inference registry maps (AssumptionKey, Op type) pairs to lists of inference functions.
72
+ # Rules are tried in registration order; the first to return TRUE wins.
73
+ ASSUMPTION_INFER_REGISTRY: dict[tuple[AssumptionKey, type], list[InferFactFn]] = {}
74
+
75
+ # Registry mapping assumptions to other assumptions they imply. For example, a "diagonal" matrix is also "symmetric"
76
+ # and "triangular". This is consulted after all other inference rules to derive additional facts.
77
+ IMPLIES: dict[AssumptionKey, list[AssumptionKey]] = {}
78
+
79
+
80
+ def register_implies(stronger: AssumptionKey, *weaker: AssumptionKey) -> None:
81
+ """Declare that *stronger* being TRUE implies each *weaker* key is also TRUE."""
82
+ IMPLIES.setdefault(stronger, []).extend(weaker)
83
+
84
+
85
+ # Maps a key to a function that infers it directly from a literal TensorConstant's data.
86
+ # AssumptionFeature consults this for variables with no owner, letting a property be
87
+ # derived from raw values (e.g. recognising a constant diagonal matrix).
88
+ ConstantInferFn = Callable[[TensorConstant], FactState]
89
+ CONSTANT_INFER_REGISTRY: dict[AssumptionKey, ConstantInferFn] = {}
90
+
91
+
92
+ def register_constant_inference(key: AssumptionKey, fn: ConstantInferFn) -> None:
93
+ """Register *fn* as the literal-constant inference for *key*."""
94
+ CONSTANT_INFER_REGISTRY[key] = fn
95
+
96
+
97
+ # The canonical structural-property keys
98
+ DIAGONAL = AssumptionKey("diagonal", short_name="diag")
99
+ LOWER_TRIANGULAR = AssumptionKey("lower_triangular", short_name="tril")
100
+ UPPER_TRIANGULAR = AssumptionKey("upper_triangular", short_name="triu")
101
+ SYMMETRIC = AssumptionKey("symmetric", short_name="sym")
102
+ POSITIVE_DEFINITE = AssumptionKey("positive_definite", short_name="pd")
103
+ ORTHOGONAL = AssumptionKey("orthogonal", short_name="orth")
104
+
105
+ ALL_KEYS = (
106
+ DIAGONAL,
107
+ LOWER_TRIANGULAR,
108
+ UPPER_TRIANGULAR,
109
+ SYMMETRIC,
110
+ POSITIVE_DEFINITE,
111
+ ORTHOGONAL,
112
+ )
113
+
114
+ # Implications about structural properties derivably from other structural properties
115
+ register_implies(DIAGONAL, LOWER_TRIANGULAR, UPPER_TRIANGULAR, SYMMETRIC)
116
+ register_implies(POSITIVE_DEFINITE, SYMMETRIC)
117
+
118
+
119
+ def register_assumption(
120
+ key: AssumptionKey, *op_types: type
121
+ ) -> Callable[[InferFactFn], InferFactFn]:
122
+ """Decorator that registers an inference rule for ``(key, op_type)`` pairs.
123
+
124
+ The decorated function is called as ``fn(key, op, feature, fgraph, node, input_states)``
125
+ and must return a list of :class:`FactState` with one entry per node output.
126
+ """
127
+
128
+ def decorator(fn: InferFactFn) -> InferFactFn:
129
+ for op_type in op_types:
130
+ ASSUMPTION_INFER_REGISTRY.setdefault((key, op_type), []).append(fn)
131
+ return fn
132
+
133
+ return decorator
134
+
135
+
136
+ def infer_assumption_for_node(
137
+ key: AssumptionKey,
138
+ op: Op,
139
+ feature: "AssumptionFeature",
140
+ fgraph: FunctionGraph,
141
+ node: Apply,
142
+ input_states: list[FactState],
143
+ ) -> list[FactState]:
144
+ """Determine the *key* fact for every output of *node*.
145
+
146
+ Walks the Op's MRO for the most specific :func:`register_assumption` rule and
147
+ returns the first non-UNKNOWN result. Falls back to ``UNKNOWN`` per output.
148
+ """
149
+ rules: list[InferFactFn] = []
150
+ for cls in type(op).__mro__:
151
+ fns = ASSUMPTION_INFER_REGISTRY.get((key, cls))
152
+ if fns is not None:
153
+ rules = fns
154
+ break
155
+
156
+ n_outputs = len(node.outputs)
157
+ for fn in rules:
158
+ output_states = fn(key, op, feature, fgraph, node, input_states)
159
+ if len(output_states) != n_outputs:
160
+ raise ValueError(
161
+ f"an assumption rule returned {len(output_states)} states for "
162
+ f"{n_outputs} outputs on node {node!r}"
163
+ )
164
+ if any(s is not FactState.UNKNOWN for s in output_states):
165
+ return output_states
166
+
167
+ return [FactState.UNKNOWN] * n_outputs
168
+
169
+
170
+ class AssumptionFeature(Feature):
171
+ """``FunctionGraph`` feature that tracks symbolic assumptions about variables.
172
+
173
+ Assumptions (e.g. "this matrix is diagonal") are represented as ``(variable,
174
+ AssumptionKey) -> FactState`` mappings. Facts are inferred lazily via per-Op
175
+ rules registered with :func:`register_assumption`.
176
+
177
+ Attached lazily by :func:`check_assumption` on first use.
178
+ Results are cached and automatically refreshed when the graph changes.
179
+ """
180
+
181
+ __slots__ = ("_stale_vars", "_var_to_keys", "cache", "fgraph")
182
+
183
+ def on_attach(self, fgraph: Any) -> None:
184
+ if hasattr(fgraph, "assumption_feature"):
185
+ raise AlreadyThere("AssumptionFeature is already attached")
186
+ self.fgraph = fgraph
187
+ self.cache: dict[tuple[Any, AssumptionKey], FactState] = {}
188
+ self._var_to_keys: dict[Any, set[AssumptionKey]] = {}
189
+ self._stale_vars: set = set()
190
+ fgraph.assumption_feature = self
191
+
192
+ def on_detach(self, fgraph: Any) -> None:
193
+ self.cache = {}
194
+ self._var_to_keys = {}
195
+ self._stale_vars = set()
196
+ self.fgraph = None
197
+ del fgraph.assumption_feature
198
+
199
+ def on_change_input(self, fgraph, node, i, old_var, new_var, reason=None) -> None:
200
+ # Carry non-UNKNOWN facts from old_var to new_var, then defer downstream
201
+ # UNKNOWN invalidation to the next get()/check() call.
202
+ # CONFLICT is cached, not raised: the graph mutation has already landed,
203
+ # so raising here corrupts the caller's view.
204
+ for key in self._var_to_keys.get(old_var, ()):
205
+ old_state = self.cache.get((old_var, key))
206
+ if old_state is None or old_state is FactState.UNKNOWN:
207
+ continue
208
+ new_state = self.cache.get((new_var, key))
209
+ if new_state is None or new_state is FactState.UNKNOWN:
210
+ self.cache[(new_var, key)] = old_state
211
+ else:
212
+ self.cache[(new_var, key)] = FactState.join(old_state, new_state)
213
+ self._var_to_keys.setdefault(new_var, set()).add(key)
214
+
215
+ self._stale_vars.update(node.outputs)
216
+
217
+ def on_prune(self, fgraph, node, reason=None) -> None:
218
+ self._stale_vars.difference_update(node.outputs)
219
+
220
+ def clone(self) -> "AssumptionFeature":
221
+ # Cache entries are keyed on variable identity, so the cloned feature starts empty
222
+ # and re-derives facts from the cloned graph (assumptions attached via ``assume()``
223
+ # follow their variables through the clone since they live as graph nodes).
224
+ return AssumptionFeature()
225
+
226
+ def get(self, var: Any, key: AssumptionKey) -> FactState:
227
+ """Return the inferred :class:`FactState` for ``(var, key)``.
228
+
229
+ Raise :class:`ConflictingAssumptionsError` when the cached state is
230
+ :attr:`FactState.CONFLICT` — surfaces conflicts from substitutions or
231
+ owner-inferred rules at the point of query, where the graph is stable
232
+ and the caller can react.
233
+ """
234
+ cache_key = (var, key)
235
+ state = self.cache.get(cache_key)
236
+ if state is None or state is FactState.UNKNOWN:
237
+ if self._stale_vars:
238
+ self._drop_unknown_downstream(self._stale_vars)
239
+ self._stale_vars.clear()
240
+ # Re-check: the UNKNOWN will still be valid
241
+ # if the stale vars were not ancestors of var.
242
+ state = self.cache.get(cache_key)
243
+ if state is None:
244
+ state = self._compute(var, key)
245
+ if state is FactState.CONFLICT:
246
+ raise ConflictingAssumptionsError(
247
+ f"Conflicting evidence for key {key!r} on {var!r}."
248
+ )
249
+ return state
250
+
251
+ def check(self, var: Any, key: AssumptionKey) -> bool:
252
+ """Return ``True`` iff the assumption is definitively TRUE for ``var``."""
253
+ return self.get(var, key) is FactState.TRUE
254
+
255
+ def _compute(self, var: Any, key: AssumptionKey) -> FactState:
256
+ """Infer the fact state for ``(var, key)``, caching ancestors inputs-first.
257
+
258
+ After owner-based inference, walks the ``IMPLIES`` graph in both directions:
259
+ - Forward: a stronger key being TRUE makes this (weaker) key TRUE.
260
+ - Contrapositive: a weaker key being FALSE makes this (stronger) key FALSE.
261
+
262
+ Raise :class:`ConflictingAssumptionsError` if owner-inferred rules produce
263
+ ``FactState.CONFLICT``.
264
+ """
265
+
266
+ def deps(v: Any) -> tuple:
267
+ # Stop descending at cached variables — their facts (and their ancestors')
268
+ # are already settled for this key.
269
+ if (v, key) in self.cache:
270
+ return ()
271
+ owner = getattr(v, "owner", None)
272
+ return tuple(owner.inputs) if owner is not None else ()
273
+
274
+ for v in walk_toposort([var], deps):
275
+ if (v, key) in self.cache:
276
+ continue
277
+
278
+ # Cache UNKNOWN up front so any recursive ``self.get(v, other_key)`` triggered
279
+ # by the IMPLIES checks sees a placeholder instead of recursing back here.
280
+ self.cache[(v, key)] = FactState.UNKNOWN
281
+ self._var_to_keys.setdefault(v, set()).add(key)
282
+
283
+ owner = getattr(v, "owner", None)
284
+ if owner is not None:
285
+ input_states = [self.get(inp, key) for inp in owner.inputs]
286
+ output_states = infer_assumption_for_node(
287
+ key, owner.op, self, self.fgraph, owner, input_states
288
+ )
289
+ state = output_states[owner.outputs.index(v)]
290
+ elif isinstance(v, TensorConstant) and key in CONSTANT_INFER_REGISTRY:
291
+ state = CONSTANT_INFER_REGISTRY[key](v)
292
+ else:
293
+ state = FactState.UNKNOWN
294
+
295
+ if state is FactState.UNKNOWN:
296
+ for stronger, weaker_list in IMPLIES.items():
297
+ if key in weaker_list and self.get(v, stronger) is FactState.TRUE:
298
+ state = FactState.TRUE
299
+ break
300
+
301
+ if state is FactState.UNKNOWN:
302
+ for weaker in IMPLIES.get(key, ()):
303
+ if self.get(v, weaker) is FactState.FALSE:
304
+ state = FactState.FALSE
305
+ break
306
+
307
+ if state is FactState.CONFLICT:
308
+ raise ConflictingAssumptionsError(
309
+ f"Conflicting evidence for {key} on {v!r} from owner-inferred rules."
310
+ )
311
+
312
+ self.cache[(v, key)] = state
313
+
314
+ return self.cache[(var, key)]
315
+
316
+ def _drop_unknown_downstream(self, start_vars) -> None:
317
+ """Drop UNKNOWN entries for *start_vars* and downstream so they re-probe."""
318
+ cache = self.cache
319
+ var_to_keys = self._var_to_keys
320
+ clients = self.fgraph.clients
321
+ queue = deque(start_vars)
322
+ seen = set(start_vars)
323
+ while queue:
324
+ var = queue.popleft()
325
+ keys = var_to_keys.get(var)
326
+ if keys:
327
+ for key in [
328
+ k for k in keys if cache.get((var, k)) is FactState.UNKNOWN
329
+ ]:
330
+ cache.pop((var, key), None)
331
+ keys.discard(key)
332
+ if not keys:
333
+ var_to_keys.pop(var, None)
334
+ for client_node, _ in clients.get(var, ()):
335
+ for out in client_node.outputs:
336
+ if out not in seen:
337
+ seen.add(out)
338
+ queue.append(out)
339
+
340
+
341
+ def true_if(cond: bool, else_false: bool = False) -> list[FactState]:
342
+ """``[TRUE]`` when *cond* holds, ``[UNKNOWN]`` (or ``[FALSE]``) otherwise."""
343
+ if cond:
344
+ return [FactState.TRUE]
345
+ return [FactState.FALSE] if else_false else [FactState.UNKNOWN]
346
+
347
+
348
+ def propagate_first(key, op, feature, fgraph, node, input_states) -> list[FactState]:
349
+ """Output inherits the assumption iff the first input has it."""
350
+ return true_if(input_states[0] is FactState.TRUE)
351
+
352
+
353
+ def all_inputs_have_key(
354
+ key, op, feature, fgraph, node, input_states
355
+ ) -> list[FactState]:
356
+ """Output inherits the assumption iff *every* input has it."""
357
+ return true_if(all(s is FactState.TRUE for s in input_states))
358
+
359
+
360
+ def check_assumption(
361
+ fgraph: FunctionGraph | None, var: Any, key: AssumptionKey
362
+ ) -> bool:
363
+ """Return True iff *key* is definitively TRUE for *var* in *fgraph*.
364
+
365
+ Lazily attaches :class:`AssumptionFeature` to *fgraph* if it is not already present.
366
+ """
367
+ if fgraph is None:
368
+ return False
369
+ feature = getattr(fgraph, "assumption_feature", None)
370
+ if feature is None:
371
+ feature = AssumptionFeature()
372
+ fgraph.attach_feature(feature)
373
+ return feature.check(var, key)