danielutils 1.1.28__py3-none-any.whl

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 (287) hide show
  1. danielutils/__init__.py +50 -0
  2. danielutils/abstractions/__init__.py +3 -0
  3. danielutils/abstractions/db/__init__.py +10 -0
  4. danielutils/abstractions/db/database.py +206 -0
  5. danielutils/abstractions/db/database_definitions.py +219 -0
  6. danielutils/abstractions/db/database_exceptions.py +34 -0
  7. danielutils/abstractions/db/database_factory.py +70 -0
  8. danielutils/abstractions/db/database_initializer.py +261 -0
  9. danielutils/abstractions/db/database_models.py +213 -0
  10. danielutils/abstractions/db/dependencies.py +6 -0
  11. danielutils/abstractions/db/implementations/__init__.py +4 -0
  12. danielutils/abstractions/db/implementations/in_memory_database.py +365 -0
  13. danielutils/abstractions/db/implementations/persistent_in_memory_database.py +174 -0
  14. danielutils/abstractions/db/implementations/redis_database.py +403 -0
  15. danielutils/abstractions/db/implementations/sqlite_database.py +491 -0
  16. danielutils/abstractions/multiprogramming/__init__.py +3 -0
  17. danielutils/abstractions/multiprogramming/multi_id.py +26 -0
  18. danielutils/abstractions/multiprogramming/worker.py +104 -0
  19. danielutils/abstractions/multiprogramming/worker_pool.py +98 -0
  20. danielutils/abstractions/repl.py +125 -0
  21. danielutils/aliases.py +174 -0
  22. danielutils/async_/__init__.py +7 -0
  23. danielutils/async_/async_cmd.py +28 -0
  24. danielutils/async_/async_command.py +879 -0
  25. danielutils/async_/async_layered_command.py +125 -0
  26. danielutils/async_/async_retry_executor.py +86 -0
  27. danielutils/async_/async_worker_pool.py +110 -0
  28. danielutils/async_/time_strategy.py +73 -0
  29. danielutils/async_/utils.py +57 -0
  30. danielutils/better_builtins/__init__.py +3 -0
  31. danielutils/better_builtins/counter.py +48 -0
  32. danielutils/better_builtins/frange.py +266 -0
  33. danielutils/better_builtins/typed_builtins/__init__.py +4 -0
  34. danielutils/better_builtins/typed_builtins/factory.py +126 -0
  35. danielutils/better_builtins/typed_builtins/tdict.py +36 -0
  36. danielutils/better_builtins/typed_builtins/tlist.py +223 -0
  37. danielutils/better_builtins/typed_builtins/tset.py +67 -0
  38. danielutils/better_builtins/typed_builtins/ttuple.py +15 -0
  39. danielutils/colors.py +203 -0
  40. danielutils/context_managers/__init__.py +5 -0
  41. danielutils/context_managers/attr_context.py +40 -0
  42. danielutils/context_managers/multi_context.py +34 -0
  43. danielutils/context_managers/optional_context.py +33 -0
  44. danielutils/context_managers/state_context.py +25 -0
  45. danielutils/context_managers/temporary_file.py +108 -0
  46. danielutils/convenience.py +12 -0
  47. danielutils/conversions/__init__.py +2 -0
  48. danielutils/conversions/main_conversions.py +131 -0
  49. danielutils/conversions/specialized_conversions/__init__.py +2 -0
  50. danielutils/conversions/specialized_conversions/to_hex.py +25 -0
  51. danielutils/conversions/specialized_conversions/to_int.py +19 -0
  52. danielutils/custom_types.py +18 -0
  53. danielutils/data_structures/__init__.py +8 -0
  54. danielutils/data_structures/algorithms.py +52 -0
  55. danielutils/data_structures/comparer.py +47 -0
  56. danielutils/data_structures/default_dict.py +23 -0
  57. danielutils/data_structures/functions.py +35 -0
  58. danielutils/data_structures/graph/__init__.py +4 -0
  59. danielutils/data_structures/graph/binary_node.py +71 -0
  60. danielutils/data_structures/graph/graph.py +208 -0
  61. danielutils/data_structures/graph/multinode.py +101 -0
  62. danielutils/data_structures/graph/node.py +60 -0
  63. danielutils/data_structures/heap/__init__.py +3 -0
  64. danielutils/data_structures/heap/heap.py +119 -0
  65. danielutils/data_structures/heap/max_heap.py +18 -0
  66. danielutils/data_structures/heap/min_heap.py +18 -0
  67. danielutils/data_structures/queue/__init__.py +3 -0
  68. danielutils/data_structures/queue/atomic_queue.py +15 -0
  69. danielutils/data_structures/queue/priority_queue.py +110 -0
  70. danielutils/data_structures/queue/queue.py +92 -0
  71. danielutils/data_structures/stack.py +102 -0
  72. danielutils/data_structures/trees/__init__.py +2 -0
  73. danielutils/data_structures/trees/binary_syntax_tree.py +84 -0
  74. danielutils/data_structures/trees/binary_tree.py +63 -0
  75. danielutils/date.py +43 -0
  76. danielutils/date_time.py +23 -0
  77. danielutils/decorators/__init__.py +18 -0
  78. danielutils/decorators/atomic.py +45 -0
  79. danielutils/decorators/attach.py +61 -0
  80. danielutils/decorators/chain_decorators.py +39 -0
  81. danielutils/decorators/decorate_conditionally.py +32 -0
  82. danielutils/decorators/delay_call.py +47 -0
  83. danielutils/decorators/deprecate.py +46 -0
  84. danielutils/decorators/final.py +39 -0
  85. danielutils/decorators/limit_recursion.py +63 -0
  86. danielutils/decorators/memo.py +68 -0
  87. danielutils/decorators/normalize_decorator.py +55 -0
  88. danielutils/decorators/overload.py +204 -0
  89. danielutils/decorators/partially_implemented.py +32 -0
  90. danielutils/decorators/processify.py +74 -0
  91. danielutils/decorators/property.py +42 -0
  92. danielutils/decorators/singleton.py +59 -0
  93. danielutils/decorators/threadify.py +40 -0
  94. danielutils/decorators/timeout.py +82 -0
  95. danielutils/decorators/total_ordering.py +82 -0
  96. danielutils/decorators/validate.py +227 -0
  97. danielutils/exceptions.py +90 -0
  98. danielutils/functions/__init__.py +13 -0
  99. danielutils/functions/areoneof.py +47 -0
  100. danielutils/functions/check_foreach.py +39 -0
  101. danielutils/functions/factorial.py +24 -0
  102. danielutils/functions/flatten.py +33 -0
  103. danielutils/functions/foreach.py +20 -0
  104. danielutils/functions/isoftype.py +475 -0
  105. danielutils/functions/isoneof.py +71 -0
  106. danielutils/functions/multiloop.py +51 -0
  107. danielutils/functions/parallel_for.py +47 -0
  108. danielutils/functions/partition.py +45 -0
  109. danielutils/functions/powerset.py +31 -0
  110. danielutils/functions/subseteq.py +25 -0
  111. danielutils/functions/types_subseteq.py +51 -0
  112. danielutils/generators/__init__.py +3 -0
  113. danielutils/generators/conditional_generator.py +59 -0
  114. danielutils/generators/generator_from_stream.py +31 -0
  115. danielutils/generators/join_generators.py +109 -0
  116. danielutils/internet.py +92 -0
  117. danielutils/io_.py +451 -0
  118. danielutils/java/__init__.py +18 -0
  119. danielutils/java/interfaces/__init__.py +1 -0
  120. danielutils/java/interfaces/comparable.py +31 -0
  121. danielutils/java/java_interface.py +59 -0
  122. danielutils/logging_/__init__.py +2 -0
  123. danielutils/logging_/_impl/__init__.py +4 -0
  124. danielutils/logging_/_impl/builtin_impls/__init__.py +2 -0
  125. danielutils/logging_/_impl/builtin_impls/file_logger.py +35 -0
  126. danielutils/logging_/_impl/builtin_impls/print_logger.py +21 -0
  127. danielutils/logging_/_impl/log_level.py +13 -0
  128. danielutils/logging_/_impl/logger.py +109 -0
  129. danielutils/logging_/_impl/logger_strategy_impl_base.py +37 -0
  130. danielutils/logging_/utils.py +199 -0
  131. danielutils/lombok/__init__.py +1 -0
  132. danielutils/lombok/builder.py +68 -0
  133. danielutils/math_/__init__.py +2 -0
  134. danielutils/math_/constants.py +103 -0
  135. danielutils/math_/functions.py +34 -0
  136. danielutils/math_/math_print.py +52 -0
  137. danielutils/math_/math_symbols.py +125 -0
  138. danielutils/metaclasses/__init__.py +5 -0
  139. danielutils/metaclasses/atomic_class_meta.py +36 -0
  140. danielutils/metaclasses/implicit_data_deleter_meta.py +87 -0
  141. danielutils/metaclasses/instance_cache_meta.py +66 -0
  142. danielutils/metaclasses/interface.py +328 -0
  143. danielutils/metaclasses/overload_meta.py +48 -0
  144. danielutils/mock_/__init__.py +1 -0
  145. danielutils/mock_/mock_module.py +27 -0
  146. danielutils/path.py +55 -0
  147. danielutils/print_.py +123 -0
  148. danielutils/progress_bar/__init__.py +3 -0
  149. danielutils/progress_bar/ascii_progress_bar.py +161 -0
  150. danielutils/progress_bar/progress_bar.py +75 -0
  151. danielutils/progress_bar/progress_bar_pool.py +72 -0
  152. danielutils/protocols/__init__.py +3 -0
  153. danielutils/protocols/dictable.py +38 -0
  154. danielutils/protocols/evaluable.py +26 -0
  155. danielutils/protocols/serializable.py +51 -0
  156. danielutils/py.typed +0 -0
  157. danielutils/random_.py +47 -0
  158. danielutils/reflection/__init__.py +6 -0
  159. danielutils/reflection/declarations.py +288 -0
  160. danielutils/reflection/file/__init__.py +1 -0
  161. danielutils/reflection/file/file_reflection.py +85 -0
  162. danielutils/reflection/function/__init__.py +1 -0
  163. danielutils/reflection/function/function_reflections.py +152 -0
  164. danielutils/reflection/info_classes/__init__.py +6 -0
  165. danielutils/reflection/info_classes/argument_info.py +278 -0
  166. danielutils/reflection/info_classes/class_info.py +204 -0
  167. danielutils/reflection/info_classes/decorator_info.py +54 -0
  168. danielutils/reflection/info_classes/file_info.py +1180 -0
  169. danielutils/reflection/info_classes/folder_info.py +592 -0
  170. danielutils/reflection/info_classes/function_info.py +551 -0
  171. danielutils/reflection/info_classes/import_info.py +207 -0
  172. danielutils/reflection/interpreter/__init__.py +9 -0
  173. danielutils/reflection/interpreter/callstack.py +57 -0
  174. danielutils/reflection/interpreter/get_traceback.py +27 -0
  175. danielutils/reflection/interpreter/interpreter.py +41 -0
  176. danielutils/reflection/interpreter/is_debugging.py +13 -0
  177. danielutils/reflection/interpreter/os_.py +33 -0
  178. danielutils/reflection/interpreter/packages.py +33 -0
  179. danielutils/reflection/interpreter/python_version.py +33 -0
  180. danielutils/reflection/interpreter/signals.py +18 -0
  181. danielutils/reflection/interpreter/tracer.py +175 -0
  182. danielutils/reflection/module/__init__.py +3 -0
  183. danielutils/reflection/module/lazy_module.py +104 -0
  184. danielutils/reflection/module/module_reflections.py +34 -0
  185. danielutils/reflection/module/package_reflection.py +161 -0
  186. danielutils/retry_executor/__init__.py +3 -0
  187. danielutils/retry_executor/backoff_strategies/__init__.py +6 -0
  188. danielutils/retry_executor/backoff_strategies/constant_backoff.py +25 -0
  189. danielutils/retry_executor/backoff_strategies/exponential_backoff.py +26 -0
  190. danielutils/retry_executor/backoff_strategies/functional_backoff.py +29 -0
  191. danielutils/retry_executor/backoff_strategies/linear_backoff.py +23 -0
  192. danielutils/retry_executor/backoff_strategies/multiplicative_backoff.py +23 -0
  193. danielutils/retry_executor/backoff_strategies/no_backoff.py +17 -0
  194. danielutils/retry_executor/backoff_strategy.py +28 -0
  195. danielutils/retry_executor/retry_executor.py +55 -0
  196. danielutils/snippets/__init__.py +1 -0
  197. danielutils/snippets/try_get.py +28 -0
  198. danielutils/system/__init__.py +3 -0
  199. danielutils/system/independent.py +197 -0
  200. danielutils/system/layered_command.py +130 -0
  201. danielutils/system/windows/__init__.py +2 -0
  202. danielutils/system/windows/utils/__init__.py +1 -0
  203. danielutils/system/windows/utils/filetime.py +19 -0
  204. danielutils/system/windows/win32_ctime.py +183 -0
  205. danielutils/system/windows/windows.py +78 -0
  206. danielutils/testing/__init__.py +1 -0
  207. danielutils/testing/unittest_/__init__.py +2 -0
  208. danielutils/testing/unittest_/always_teardown_testcase.py +75 -0
  209. danielutils/testing/unittest_/auto_cwd_testcase.py +114 -0
  210. danielutils/text.py +169 -0
  211. danielutils/time.py +64 -0
  212. danielutils/tools/__init__.py +1 -0
  213. danielutils/tools/unittest_test_runner/__init__.py +9 -0
  214. danielutils/tools/unittest_test_runner/cli.py +70 -0
  215. danielutils/tools/unittest_test_runner/core/__init__.py +17 -0
  216. danielutils/tools/unittest_test_runner/core/discovery.py +322 -0
  217. danielutils/tools/unittest_test_runner/core/execution.py +244 -0
  218. danielutils/tools/unittest_test_runner/core/parser.py +163 -0
  219. danielutils/tools/unittest_test_runner/core/runner.py +561 -0
  220. danielutils/tools/unittest_test_runner/models.py +82 -0
  221. danielutils/university/__init__.py +7 -0
  222. danielutils/university/computability_and_complexity/__init__.py +2 -0
  223. danielutils/university/computability_and_complexity/discreate_finite_automaton.py +89 -0
  224. danielutils/university/computability_and_complexity/languages/__init__.py +0 -0
  225. danielutils/university/computability_and_complexity/languages/language.py +17 -0
  226. danielutils/university/computability_and_complexity/languages/sat.py +71 -0
  227. danielutils/university/computability_and_complexity/turing_machine.py +47 -0
  228. danielutils/university/databases/__init__.py +1 -0
  229. danielutils/university/databases/all.py +756 -0
  230. danielutils/university/image_proccesing/__init__.py +1 -0
  231. danielutils/university/image_proccesing/encoding/__init__.py +3 -0
  232. danielutils/university/image_proccesing/encoding/encoding.py +35 -0
  233. danielutils/university/image_proccesing/encoding/lossless/__init__.py +4 -0
  234. danielutils/university/image_proccesing/encoding/lossless/huffman.py +0 -0
  235. danielutils/university/image_proccesing/encoding/lossless/lossless_encoding.py +10 -0
  236. danielutils/university/image_proccesing/encoding/lossless/lzw.py +0 -0
  237. danielutils/university/image_proccesing/encoding/lossless/run_length.py +22 -0
  238. danielutils/university/image_proccesing/encoding/lossy/__init__.py +1 -0
  239. danielutils/university/image_proccesing/encoding/lossy/lossy_encoding.py +10 -0
  240. danielutils/university/image_proccesing/tansformations/__init__.py +0 -0
  241. danielutils/university/image_proccesing/tansformations/gaussian.py +0 -0
  242. danielutils/university/image_proccesing/tansformations/gradient.py +0 -0
  243. danielutils/university/image_proccesing/tansformations/hough.py +0 -0
  244. danielutils/university/image_proccesing/tansformations/laplacian.py +0 -0
  245. danielutils/university/image_proccesing/tansformations/transformation.py +11 -0
  246. danielutils/university/linear_algebra/__init__.py +1 -0
  247. danielutils/university/linear_algebra/matrix.py +404 -0
  248. danielutils/university/machine_learning/__init__.py +0 -0
  249. danielutils/university/machine_learning/activation_functions/__init__.py +2 -0
  250. danielutils/university/machine_learning/activation_functions/activation_function.py +11 -0
  251. danielutils/university/machine_learning/activation_functions/relu.py +11 -0
  252. danielutils/university/machine_learning/neuron.py +15 -0
  253. danielutils/university/oop/__init__.py +1 -0
  254. danielutils/university/oop/observer.py +31 -0
  255. danielutils/university/oop/strategy.py +9 -0
  256. danielutils/university/probability/__init__.py +7 -0
  257. danielutils/university/probability/conditional_variable/__init__.py +3 -0
  258. danielutils/university/probability/conditional_variable/conditional_variable.py +138 -0
  259. danielutils/university/probability/conditional_variable/continuous/__init__.py +0 -0
  260. danielutils/university/probability/conditional_variable/discrete/__init__.py +8 -0
  261. danielutils/university/probability/conditional_variable/discrete/bernoulli.py +38 -0
  262. danielutils/university/probability/conditional_variable/discrete/bernoulli_sum.py +31 -0
  263. danielutils/university/probability/conditional_variable/discrete/binomial.py +40 -0
  264. danielutils/university/probability/conditional_variable/discrete/conditional_from_discrete_probability_func.py +36 -0
  265. danielutils/university/probability/conditional_variable/discrete/discrete.py +42 -0
  266. danielutils/university/probability/conditional_variable/discrete/geometric.py +43 -0
  267. danielutils/university/probability/conditional_variable/discrete/poisson.py +32 -0
  268. danielutils/university/probability/conditional_variable/discrete/uniform.py +33 -0
  269. danielutils/university/probability/distributions.py +35 -0
  270. danielutils/university/probability/expressions/__init__.py +2 -0
  271. danielutils/university/probability/expressions/accumulation_expression.py +291 -0
  272. danielutils/university/probability/expressions/probability_expression.py +110 -0
  273. danielutils/university/probability/funcs/__init__.py +4 -0
  274. danielutils/university/probability/funcs/covariance.py +12 -0
  275. danielutils/university/probability/funcs/expected_value.py +41 -0
  276. danielutils/university/probability/funcs/probability_function.py +14 -0
  277. danielutils/university/probability/funcs/variance.py +17 -0
  278. danielutils/university/probability/operator.py +79 -0
  279. danielutils/university/probability/protocols.py +32 -0
  280. danielutils/university/probability/supp.py +101 -0
  281. danielutils/university/probability/transformation.py +44 -0
  282. danielutils/versioned_imports.py +31 -0
  283. danielutils-1.1.28.dist-info/METADATA +390 -0
  284. danielutils-1.1.28.dist-info/RECORD +287 -0
  285. danielutils-1.1.28.dist-info/WHEEL +5 -0
  286. danielutils-1.1.28.dist-info/licenses/LICENSE +21 -0
  287. danielutils-1.1.28.dist-info/top_level.txt +1 -0
@@ -0,0 +1,291 @@
1
+ import copy
2
+ from fractions import Fraction
3
+ from typing import Callable, Any, Union, Optional
4
+ from .probability_expression import ProbabilityExpression
5
+ from ..protocols import Evaluable, Equatable
6
+ from ..operator import Operator
7
+ from ....data_structures import BinarySyntaxTree as BST
8
+
9
+ def _create_operator(op: Operator, reverse: bool = False) -> Callable[['AccumulationExpression', Any], Evaluable]:
10
+ def operator(self, other) -> Evaluable:
11
+ lhs, rhs = self, other
12
+ if reverse:
13
+ lhs, rhs = rhs, lhs
14
+ if isinstance(other, (int, float, Fraction)) and op in Operator.order_operators():
15
+ rhs_expr = ProbabilityExpression(lhs.lhs, op, other) if op in Operator.inequalities() else ProbabilityExpression(other)
16
+ return AccumulationExpression(lhs, Operator.AND, rhs_expr)
17
+ raise NotImplementedError("Not Implemented")
18
+
19
+ return operator
20
+
21
+ class AccumulationExpression(Evaluable):
22
+ @staticmethod
23
+ def _to_leaf(value: Any) -> BST.Node:
24
+ if isinstance(value, BST.Node):
25
+ return value
26
+ return BST.Node(value)
27
+
28
+ @staticmethod
29
+ def _probability_expression_to_nodes(expr: ProbabilityExpression) -> BST.Node: # type:ignore
30
+ if expr.op is None:
31
+ return AccumulationExpression._to_leaf(expr.lhs)
32
+ return BST.Node(
33
+ expr.op,
34
+ AccumulationExpression._to_leaf(expr.lhs),
35
+ AccumulationExpression._to_leaf(expr.rhs),
36
+ )
37
+
38
+
39
+ @staticmethod
40
+ def _expression_intersection(expr1: ProbabilityExpression, expr2: ProbabilityExpression) -> Fraction:
41
+ if expr1.is_equal(expr2):
42
+ return expr1.evaluate()
43
+
44
+ a = expr1.rhs
45
+ op1 = expr1.op
46
+ b = expr2.rhs
47
+ op2 = expr2.op
48
+ X = expr1.lhs
49
+ if op1 in Operator.greater_than_inequalities() and op2 in Operator.less_than_inequalities():
50
+ return X.between(a, b, op1, op2)
51
+
52
+ if op1 in Operator.greater_than_inequalities() and op2 in Operator.greater_than_inequalities():
53
+ op = op1
54
+ if op2 == Operator.GT:
55
+ op = op2
56
+ return X.evaluate(max(a, b), op)
57
+
58
+ if op1 in Operator.less_than_inequalities() and op2 in Operator.less_than_inequalities():
59
+ op = op1
60
+ if op2 == Operator.LT:
61
+ op = op2
62
+ return X.evaluate(min(a, b), op)
63
+
64
+ if op1 in Operator.equalities() and op2 in Operator.equalities():
65
+ return Fraction(0, 1)
66
+
67
+ if op1 in Operator.equalities():
68
+ return expr1.evaluate()
69
+ return expr2.evaluate()
70
+
71
+ def __init__(self, lhs: ProbabilityExpression, op: Operator, rhs: ProbabilityExpression) -> None:
72
+ l = AccumulationExpression._probability_expression_to_nodes(lhs)
73
+ r = AccumulationExpression._probability_expression_to_nodes(rhs)
74
+ root = BST.Node(op, l, r)
75
+ self._tree = BST(root)
76
+
77
+ _eq_operator = _create_operator(Operator.EQ)
78
+ _ne_operator = _create_operator(Operator.NE)
79
+ _gt_operator = _create_operator(Operator.GT)
80
+ _ge_operator = _create_operator(Operator.GE)
81
+ _lt_operator = _create_operator(Operator.LT)
82
+ _le_operator = _create_operator(Operator.LE)
83
+
84
+ def __eq__(self, other) -> bool:
85
+ if isinstance(other, AccumulationExpression):
86
+ return self.is_equal(other)
87
+ return self._eq_operator(other) # type:ignore[return-value]
88
+
89
+ def __ne__(self, other) -> bool:
90
+ if isinstance(other, AccumulationExpression):
91
+ return not self.is_equal(other)
92
+ return self._ne_operator(other) # type:ignore[return-value]
93
+
94
+ def __gt__(self, other):
95
+ return self._gt_operator(other)
96
+
97
+ def __ge__(self, other):
98
+ return self._ge_operator(other)
99
+
100
+ def __lt__(self, other):
101
+ return self._lt_operator(other)
102
+
103
+ def __le__(self, other):
104
+ return self._le_operator(other)
105
+
106
+ def __bool__(self) -> bool:
107
+ if self._tree.root.data != Operator.EQ:
108
+ return False
109
+
110
+ def node_to_equatable(n: BST.Node) -> Equatable: # type:ignore
111
+ if n.depth() == 2: # type:ignore
112
+ return ProbabilityExpression(n.left.data, n.data, n.right.data) # type:ignore
113
+ pass
114
+
115
+ if (d := self._tree.depth()) == 3:
116
+ return node_to_equatable(self._tree.root.left).is_equal(node_to_equatable(self._tree.root.right))
117
+
118
+ return NotImplemented
119
+
120
+ def __reversed__(self) -> 'AccumulationExpression':
121
+ return self.reverse()
122
+
123
+ def __repr__(self) -> str:
124
+ return f"{self.__class__.__name__}(lhs={self._tree.root.left}, op={self._tree.root.data}, rhs={self._tree.root.right})"
125
+
126
+ def is_equal(self, other) -> bool:
127
+ if not isinstance(other, AccumulationExpression):
128
+ return False
129
+ from ..conditional_variable import ConditionalVariable
130
+ # I have to create a custom comparer because the BST can't (and shouldn't) know to compare using
131
+ # ConditionalVariable.is_equal
132
+ def are_nodes_equal(a: Any, b: Any) -> bool:
133
+ if not (isinstance(a, BST.Node) and isinstance(b, BST.Node)):
134
+ if a is None and b is None:
135
+ return True
136
+ return False
137
+ if isinstance(a.data, Operator) and isinstance(b.data, Operator): # type:ignore
138
+ if not (a.data == b.data): # type:ignore
139
+ return False
140
+ elif isinstance(a.data, ConditionalVariable) and isinstance(b.data, ConditionalVariable): # type:ignore
141
+ if not a.data.is_equal(b.data): # type:ignore
142
+ return False
143
+ else:
144
+ return a.data == b.data # type:ignore
145
+
146
+ return are_nodes_equal(a.left, b.left) and are_nodes_equal(a.right, b.right) # type:ignore
147
+
148
+ return are_nodes_equal(self.standardize()._tree.root, other.standardize()._tree.root)
149
+
150
+ def evaluate(self) -> Fraction:
151
+ if not self._is_valid_tree():
152
+ raise AttributeError("Expression is not valid")
153
+ root = self._tree.root
154
+ if root.data == Operator.EQ:
155
+ return self._evaluate_equality(root)
156
+ if (d := self._tree.depth()) == 3:
157
+ # example: a < X < b
158
+ # => ____&____ 1
159
+ # / \
160
+ # ___<___ ___<___ 2
161
+ # a X X b 3
162
+ # but actually it is ordered differently
163
+ # => ____&____ 1
164
+ # / \
165
+ # ___>___ ___<___ 2
166
+ # X a X b 3
167
+ a = self._tree.root.left.right.data
168
+ op1 = self._tree.root.left.data
169
+ b = self._tree.root.right.right.data
170
+ op2 = self._tree.root.right.data
171
+ X = self._tree.root.left.left.data
172
+ Y = self._tree.root.right.left.data
173
+ lhs = ProbabilityExpression(X, op1, a)
174
+ rhs = ProbabilityExpression(Y, op2, b)
175
+ main_operator = self._tree.root.data
176
+ if X is Y:
177
+ if main_operator == Operator.AND:
178
+ return AccumulationExpression._expression_intersection(lhs, rhs)
179
+ numerator: Fraction = AccumulationExpression._expression_intersection(lhs, rhs)
180
+ denominator: Fraction = rhs.evaluate()
181
+ return numerator / denominator
182
+ if X.is_dependent(Y):
183
+ raise NotImplementedError("This part is not implemented yet")
184
+ raise NotImplementedError("Illegal State?")
185
+
186
+ # example: a < X < Y < b
187
+ raise NotImplementedError("This part is not implemented yet")
188
+
189
+ @staticmethod
190
+ def _leaf_value(node: BST.Node) -> Any:
191
+ return node.data
192
+
193
+ def _evaluate_equality(self, root: BST.Node) -> Fraction:
194
+ from ..conditional_variable import ConditionalVariable
195
+
196
+ left = root.left
197
+ right = root.right
198
+ if left is None or right is None:
199
+ raise AttributeError("Expression is not valid")
200
+
201
+ right_value = self._leaf_value(right)
202
+ if isinstance(left.data, Operator) and left.left is not None and left.right is not None:
203
+ lhs_val = self._leaf_value(left.left)
204
+ rhs_val = self._leaf_value(left.right)
205
+ if isinstance(lhs_val, ConditionalVariable):
206
+ variable = lhs_val
207
+ operand = rhs_val
208
+ if left.data == Operator.ADD:
209
+ target = right_value - operand
210
+ elif left.data == Operator.SUB:
211
+ target = right_value + operand
212
+ else:
213
+ raise NotImplementedError("This part is not implemented yet")
214
+ return variable.evaluate(target, Operator.EQ)
215
+ if isinstance(rhs_val, ConditionalVariable):
216
+ variable = rhs_val
217
+ operand = lhs_val
218
+ if left.data == Operator.ADD:
219
+ target = right_value - operand
220
+ elif left.data == Operator.SUB:
221
+ target = operand - right_value
222
+ else:
223
+ raise NotImplementedError("This part is not implemented yet")
224
+ return variable.evaluate(target, Operator.EQ)
225
+
226
+ left_value = self._leaf_value(left)
227
+ if isinstance(right.data, Operator) and right.left is not None and right.right is not None:
228
+ lhs_val = self._leaf_value(right.left)
229
+ rhs_val = self._leaf_value(right.right)
230
+ if isinstance(lhs_val, ConditionalVariable):
231
+ variable = lhs_val
232
+ operand = rhs_val
233
+ if right.data == Operator.ADD:
234
+ target = left_value - operand
235
+ elif right.data == Operator.SUB:
236
+ target = left_value + operand
237
+ else:
238
+ raise NotImplementedError("This part is not implemented yet")
239
+ return variable.evaluate(target, Operator.EQ)
240
+ if isinstance(rhs_val, ConditionalVariable):
241
+ variable = rhs_val
242
+ operand = lhs_val
243
+ if right.data == Operator.ADD:
244
+ target = left_value - operand
245
+ elif right.data == Operator.SUB:
246
+ target = operand - left_value
247
+ else:
248
+ raise NotImplementedError("This part is not implemented yet")
249
+ return variable.evaluate(target, Operator.EQ)
250
+
251
+ if isinstance(left_value, ConditionalVariable):
252
+ return left_value.evaluate(right_value, Operator.EQ)
253
+ if isinstance(right_value, ConditionalVariable):
254
+ return right_value.evaluate(left_value, Operator.EQ)
255
+ raise AttributeError("Expression is not valid")
256
+
257
+ def duplicate(self) -> 'AccumulationExpression':
258
+ return copy.deepcopy(self)
259
+
260
+ def reverse(self) -> 'AccumulationExpression':
261
+ res = self.duplicate()
262
+ res._tree = res._tree.reverse()
263
+ return res
264
+
265
+ def standardize(self) -> 'AccumulationExpression':
266
+ for n in self._tree.traverse(self._tree.TraversalMode.Middle):
267
+ if n is self._tree.root:
268
+ break
269
+ if isinstance(n.data, Operator):
270
+ if n.data in Operator.greater_than_inequalities():
271
+ return self.reverse()
272
+ return self.duplicate()
273
+
274
+ def _is_valid_tree(self) -> bool:
275
+ q = [self._tree.root]
276
+ i = 0
277
+ while i < len(q):
278
+ if isinstance(q[i], BST.Node):
279
+ if not isinstance(q[i].data, Operator):
280
+ if not q[i].is_leaf:
281
+ return False
282
+ else:
283
+ q.append(q[i].left)
284
+ q.append(q[i].right)
285
+ i += 1
286
+ return True
287
+
288
+
289
+ __all__ = [
290
+ 'AccumulationExpression'
291
+ ]
@@ -0,0 +1,110 @@
1
+ from typing import Any, Callable, Optional
2
+ from fractions import Fraction
3
+ from ..protocols import Evaluable
4
+ from ..operator import Operator
5
+
6
+
7
+ def _create_operator(op: Operator, reverse: bool = False) -> Callable[['ConditionalVariable', Any], Evaluable]:
8
+ def operator(self: 'ProbabilityExpression', other: Any) -> Evaluable:
9
+ lhs, rhs = self, other
10
+ if reverse:
11
+ lhs, rhs = rhs, lhs
12
+ from .accumulation_expression import AccumulationExpression
13
+
14
+ if op in Operator.order_operators():
15
+ if isinstance(rhs, (int, float, Fraction)):
16
+ if op in Operator.inequalities():
17
+ rhs = ProbabilityExpression(lhs.lhs, op, rhs)
18
+ return AccumulationExpression(lhs, Operator.AND, rhs)
19
+ elif op == Operator.EQ:
20
+ return AccumulationExpression(lhs, op, ProbabilityExpression(rhs))
21
+ elif isinstance(rhs, (ProbabilityExpression, AccumulationExpression)):
22
+ if isinstance(other, ProbabilityExpression):
23
+ # TODO P((0<X)<(Y<5))
24
+ # I think that this is the solution
25
+ return AccumulationExpression(self, op, other)
26
+ raise NotImplementedError("Illegal state")
27
+ if op in {Operator.AND, Operator.GIVEN}:
28
+ if not isinstance(other, ProbabilityExpression):
29
+ raise NotImplementedError("Illegal state")
30
+ return AccumulationExpression(lhs, op, rhs)
31
+ raise NotImplementedError("Illegal state 2")
32
+
33
+ return operator
34
+ class ProbabilityExpression(Evaluable):
35
+ OPERATOR_TYPE = Callable[['ProbabilityExpression', Any], 'AccumulationExpression']
36
+
37
+
38
+ def __init__(self, lhs: Evaluable, op: Optional[Operator] = None, rhs: Optional[Any] = None):
39
+ self._lhs = lhs
40
+ self._op = op
41
+ self._rhs = rhs
42
+
43
+ __eq__: OPERATOR_TYPE = _create_operator(Operator.EQ)
44
+ __gt__: OPERATOR_TYPE = _create_operator(Operator.GT)
45
+ __ge__: OPERATOR_TYPE = _create_operator(Operator.GE)
46
+ __lt__: OPERATOR_TYPE = _create_operator(Operator.LT)
47
+ __le__: OPERATOR_TYPE = _create_operator(Operator.LE)
48
+ __or__: OPERATOR_TYPE = _create_operator(Operator.GIVEN)
49
+ __ror__: OPERATOR_TYPE = _create_operator(Operator.GIVEN, reverse=True)
50
+ __and__: OPERATOR_TYPE = _create_operator(Operator.AND)
51
+ __rand__: OPERATOR_TYPE = _create_operator(Operator.AND, reverse=True)
52
+
53
+ def __repr__(self) -> str:
54
+ return f"{self.__class__.__name__}({self.lhs} {self.op.value} {self.rhs})"
55
+
56
+ def __hash__(self) -> int:
57
+ return hash((self.__class__, self.lhs, self.op, self.rhs))
58
+
59
+ @property
60
+ def lhs(self):
61
+ return self._lhs
62
+
63
+ @property
64
+ def op(self):
65
+ return self._op
66
+
67
+ @property
68
+ def rhs(self):
69
+ return self._rhs
70
+
71
+ @property
72
+ def is_partial(self) -> bool:
73
+ return self.op is not None and self.rhs is not None
74
+
75
+ def evaluate(self) -> Fraction:
76
+ return self.lhs.evaluate(self.rhs, self.op)
77
+
78
+ def is_equal(self, other) -> bool:
79
+ if not isinstance(other, ProbabilityExpression):
80
+ raise TypeError(
81
+ f"Cant compare equality between {self.__class__.__qualname__} and non ConditionalExpression")
82
+ from ..conditional_variable import ConditionalVariable
83
+ if isinstance(self.lhs, ConditionalVariable) and not isinstance(other.lhs, ConditionalVariable):
84
+ return False
85
+ if not isinstance(self.lhs, ConditionalVariable) and isinstance(other.lhs, ConditionalVariable):
86
+ return False
87
+ # We need this complicated thing instead of doing self.lhs == other.lhs (and same for rhs) because we
88
+ # cant use __eq__
89
+
90
+ # TODO make better
91
+ if isinstance(self.lhs, ConditionalVariable):
92
+ if not (self.lhs.__class__ == other.lhs.__class__):
93
+ return False
94
+ else:
95
+ if not (self.lhs == other.lhs):
96
+ return False
97
+
98
+ if isinstance(self.rhs, ConditionalVariable):
99
+ if not (self.rhs.__class__ == other.rhs.__class__):
100
+ return False
101
+ else:
102
+ if not (self.rhs == other.rhs):
103
+ return False
104
+
105
+ return self.op == other.op
106
+
107
+
108
+ __all__ = [
109
+ 'ProbabilityExpression',
110
+ ]
@@ -0,0 +1,4 @@
1
+ from .covariance import *
2
+ from .expected_value import *
3
+ from .probability_function import *
4
+ from .variance import *
@@ -0,0 +1,12 @@
1
+ from fractions import Fraction
2
+ from .expected_value import expected_value as E
3
+ from ..conditional_variable import ConditionalVariable
4
+
5
+ def covariance(X: ConditionalVariable, Y: ConditionalVariable) -> Fraction:
6
+ # alternative = E((X - E(X)) * (Y - E(Y)))
7
+ return E(X * Y) - E(X) * E(Y) # type:ignore
8
+
9
+
10
+ __all__ = [
11
+ "covariance",
12
+ ]
@@ -0,0 +1,41 @@
1
+ from typing import Union, Any, Callable, Dict
2
+
3
+ from fractions import Fraction
4
+ from ..conditional_variable import ConditionalVariable
5
+ from .probability_function import probability_function as P
6
+ from ..expressions import ProbabilityExpression
7
+ from ..operator import Operator
8
+ from ..protocols import ExpectedValueCalculable
9
+
10
+ _mapping: Callable[[Any], Dict[Operator, Callable]] = lambda const: {
11
+ Operator.POW: lambda n: n ** const,
12
+ Operator.MUL: lambda n: n * const,
13
+ Operator.DIV: lambda n: n / const,
14
+ Operator.MODULUS: lambda n: n & const,
15
+ }
16
+
17
+
18
+ def expected_value(obj: Union[ConditionalVariable, ProbabilityExpression]) -> Fraction:
19
+ if isinstance(obj, ExpectedValueCalculable):
20
+ return obj.expected_value()
21
+ res = Fraction(0, 1)
22
+ if isinstance(obj, ConditionalVariable):
23
+ X = obj
24
+ for n in X.supp:
25
+ res += n * P(X == n) # type:ignore
26
+ return res
27
+ if isinstance(obj.rhs, int):
28
+ const = obj.rhs
29
+ op = obj.op
30
+ X = obj.lhs
31
+
32
+ def f(n: Union[int, float, Fraction]) -> Union[int, float, Fraction]:
33
+ return _mapping(const)[op](n)
34
+
35
+ for n in X.supp:
36
+ res += f(n) * P(X == n) # type:ignore
37
+ return res
38
+ assert False
39
+
40
+
41
+ __all__ = ['expected_value']
@@ -0,0 +1,14 @@
1
+ from ..protocols import Evaluable
2
+ from fractions import Fraction
3
+
4
+
5
+ def probability_function(*evaluables: Evaluable[Fraction]) -> Fraction:
6
+ res = Fraction(1, 1)
7
+ for evaluable in evaluables:
8
+ res *= evaluable.evaluate()
9
+ return res
10
+
11
+
12
+ __all__ = [
13
+ 'probability_function',
14
+ ]
@@ -0,0 +1,17 @@
1
+ from fractions import Fraction
2
+ from .expected_value import expected_value as E
3
+ from ..conditional_variable import ConditionalVariable
4
+ from ..protocols import VariableCalculable
5
+
6
+
7
+ def variance(obj: ConditionalVariable) -> Fraction:
8
+ if isinstance(obj, VariableCalculable):
9
+ return obj.variance()
10
+ X = obj
11
+ # alternative = E((X - E(X)) ** 2)
12
+ return E(X ** 2) - E(X) ** 2 # type:ignore
13
+
14
+
15
+ __all__ = [
16
+ 'variance'
17
+ ]
@@ -0,0 +1,79 @@
1
+ from enum import Enum
2
+ from typing import Set as Set
3
+ from ...reflection import get_python_version
4
+
5
+ if get_python_version() >= (3, 9):
6
+ from builtins import set as Set
7
+
8
+
9
+ class Operator(Enum):
10
+ """
11
+ Operator Enum to define the types of operators.
12
+ """
13
+ EQ = "=="
14
+ NE = "!="
15
+ GT = ">"
16
+ GE = ">="
17
+ LT = "<"
18
+ LE = "<="
19
+
20
+ @staticmethod
21
+ def strong_inequalities() -> Set['Operator']:
22
+ return {Operator.GT, Operator.LT}
23
+
24
+ @staticmethod
25
+ def weak_inequalities() -> Set['Operator']:
26
+ return {Operator.GE, Operator.LE}
27
+
28
+ @staticmethod
29
+ def inequalities() -> Set['Operator']:
30
+ return Operator.strong_inequalities().union(Operator.weak_inequalities())
31
+
32
+ @staticmethod
33
+ def equalities() -> Set['Operator']:
34
+ return {Operator.EQ, Operator.NE}
35
+
36
+ @staticmethod
37
+ def greater_than_inequalities() -> Set['Operator']:
38
+ return {Operator.GE, Operator.GT}
39
+
40
+ @staticmethod
41
+ def less_than_inequalities() -> Set['Operator']:
42
+ return {Operator.LE, Operator.LT}
43
+
44
+ @staticmethod
45
+ def order_operators() -> Set['Operator']:
46
+ return Operator.inequalities().union(Operator.equalities())
47
+
48
+ MUL = "*"
49
+ DIV = "/"
50
+ MODULUS = "%"
51
+ GIVEN = '|'
52
+ AND = '&'
53
+ POW = '**'
54
+ ADD = "+"
55
+ SUB = "-"
56
+
57
+ @property
58
+ def inverse(self) -> 'Operator':
59
+ """
60
+ Returns the inverse of the operator.
61
+ Returns:
62
+ Operator (Enum): the inverse of the operator.
63
+ """
64
+ dct = {
65
+ Operator.EQ: Operator.NE,
66
+ Operator.NE: Operator.EQ,
67
+ Operator.GT: Operator.LE,
68
+ Operator.LE: Operator.GT,
69
+ Operator.GE: Operator.LT,
70
+ Operator.LT: Operator.GE
71
+ }
72
+ if self not in dct:
73
+ raise ValueError(f"Operator.{self.name} does not support 'inverse'.")
74
+ return dct[self]
75
+
76
+
77
+ __all__ = [
78
+ "Operator"
79
+ ]
@@ -0,0 +1,32 @@
1
+ from fractions import Fraction
2
+ from typing import runtime_checkable, Protocol, TypeVar
3
+
4
+ T = TypeVar('T')
5
+
6
+
7
+ @runtime_checkable
8
+ class Evaluable(Protocol[T]):
9
+ def evaluate(self, *args, **kwargs) -> T: ...
10
+
11
+
12
+ @runtime_checkable
13
+ class ExpectedValueCalculable(Protocol):
14
+ def expected_value(self) -> Fraction: ...
15
+
16
+
17
+ @runtime_checkable
18
+ class VariableCalculable(Protocol):
19
+ def variance(self) -> Fraction: ...
20
+
21
+
22
+ @runtime_checkable
23
+ class Equatable(Protocol):
24
+ def is_equal(self, other) -> bool: ...
25
+
26
+
27
+ __all__ = [
28
+ 'Evaluable',
29
+ 'VariableCalculable',
30
+ "ExpectedValueCalculable",
31
+ 'Equatable',
32
+ ]