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.
- danielutils/__init__.py +50 -0
- danielutils/abstractions/__init__.py +3 -0
- danielutils/abstractions/db/__init__.py +10 -0
- danielutils/abstractions/db/database.py +206 -0
- danielutils/abstractions/db/database_definitions.py +219 -0
- danielutils/abstractions/db/database_exceptions.py +34 -0
- danielutils/abstractions/db/database_factory.py +70 -0
- danielutils/abstractions/db/database_initializer.py +261 -0
- danielutils/abstractions/db/database_models.py +213 -0
- danielutils/abstractions/db/dependencies.py +6 -0
- danielutils/abstractions/db/implementations/__init__.py +4 -0
- danielutils/abstractions/db/implementations/in_memory_database.py +365 -0
- danielutils/abstractions/db/implementations/persistent_in_memory_database.py +174 -0
- danielutils/abstractions/db/implementations/redis_database.py +403 -0
- danielutils/abstractions/db/implementations/sqlite_database.py +491 -0
- danielutils/abstractions/multiprogramming/__init__.py +3 -0
- danielutils/abstractions/multiprogramming/multi_id.py +26 -0
- danielutils/abstractions/multiprogramming/worker.py +104 -0
- danielutils/abstractions/multiprogramming/worker_pool.py +98 -0
- danielutils/abstractions/repl.py +125 -0
- danielutils/aliases.py +174 -0
- danielutils/async_/__init__.py +7 -0
- danielutils/async_/async_cmd.py +28 -0
- danielutils/async_/async_command.py +879 -0
- danielutils/async_/async_layered_command.py +125 -0
- danielutils/async_/async_retry_executor.py +86 -0
- danielutils/async_/async_worker_pool.py +110 -0
- danielutils/async_/time_strategy.py +73 -0
- danielutils/async_/utils.py +57 -0
- danielutils/better_builtins/__init__.py +3 -0
- danielutils/better_builtins/counter.py +48 -0
- danielutils/better_builtins/frange.py +266 -0
- danielutils/better_builtins/typed_builtins/__init__.py +4 -0
- danielutils/better_builtins/typed_builtins/factory.py +126 -0
- danielutils/better_builtins/typed_builtins/tdict.py +36 -0
- danielutils/better_builtins/typed_builtins/tlist.py +223 -0
- danielutils/better_builtins/typed_builtins/tset.py +67 -0
- danielutils/better_builtins/typed_builtins/ttuple.py +15 -0
- danielutils/colors.py +203 -0
- danielutils/context_managers/__init__.py +5 -0
- danielutils/context_managers/attr_context.py +40 -0
- danielutils/context_managers/multi_context.py +34 -0
- danielutils/context_managers/optional_context.py +33 -0
- danielutils/context_managers/state_context.py +25 -0
- danielutils/context_managers/temporary_file.py +108 -0
- danielutils/convenience.py +12 -0
- danielutils/conversions/__init__.py +2 -0
- danielutils/conversions/main_conversions.py +131 -0
- danielutils/conversions/specialized_conversions/__init__.py +2 -0
- danielutils/conversions/specialized_conversions/to_hex.py +25 -0
- danielutils/conversions/specialized_conversions/to_int.py +19 -0
- danielutils/custom_types.py +18 -0
- danielutils/data_structures/__init__.py +8 -0
- danielutils/data_structures/algorithms.py +52 -0
- danielutils/data_structures/comparer.py +47 -0
- danielutils/data_structures/default_dict.py +23 -0
- danielutils/data_structures/functions.py +35 -0
- danielutils/data_structures/graph/__init__.py +4 -0
- danielutils/data_structures/graph/binary_node.py +71 -0
- danielutils/data_structures/graph/graph.py +208 -0
- danielutils/data_structures/graph/multinode.py +101 -0
- danielutils/data_structures/graph/node.py +60 -0
- danielutils/data_structures/heap/__init__.py +3 -0
- danielutils/data_structures/heap/heap.py +119 -0
- danielutils/data_structures/heap/max_heap.py +18 -0
- danielutils/data_structures/heap/min_heap.py +18 -0
- danielutils/data_structures/queue/__init__.py +3 -0
- danielutils/data_structures/queue/atomic_queue.py +15 -0
- danielutils/data_structures/queue/priority_queue.py +110 -0
- danielutils/data_structures/queue/queue.py +92 -0
- danielutils/data_structures/stack.py +102 -0
- danielutils/data_structures/trees/__init__.py +2 -0
- danielutils/data_structures/trees/binary_syntax_tree.py +84 -0
- danielutils/data_structures/trees/binary_tree.py +63 -0
- danielutils/date.py +43 -0
- danielutils/date_time.py +23 -0
- danielutils/decorators/__init__.py +18 -0
- danielutils/decorators/atomic.py +45 -0
- danielutils/decorators/attach.py +61 -0
- danielutils/decorators/chain_decorators.py +39 -0
- danielutils/decorators/decorate_conditionally.py +32 -0
- danielutils/decorators/delay_call.py +47 -0
- danielutils/decorators/deprecate.py +46 -0
- danielutils/decorators/final.py +39 -0
- danielutils/decorators/limit_recursion.py +63 -0
- danielutils/decorators/memo.py +68 -0
- danielutils/decorators/normalize_decorator.py +55 -0
- danielutils/decorators/overload.py +204 -0
- danielutils/decorators/partially_implemented.py +32 -0
- danielutils/decorators/processify.py +74 -0
- danielutils/decorators/property.py +42 -0
- danielutils/decorators/singleton.py +59 -0
- danielutils/decorators/threadify.py +40 -0
- danielutils/decorators/timeout.py +82 -0
- danielutils/decorators/total_ordering.py +82 -0
- danielutils/decorators/validate.py +227 -0
- danielutils/exceptions.py +90 -0
- danielutils/functions/__init__.py +13 -0
- danielutils/functions/areoneof.py +47 -0
- danielutils/functions/check_foreach.py +39 -0
- danielutils/functions/factorial.py +24 -0
- danielutils/functions/flatten.py +33 -0
- danielutils/functions/foreach.py +20 -0
- danielutils/functions/isoftype.py +475 -0
- danielutils/functions/isoneof.py +71 -0
- danielutils/functions/multiloop.py +51 -0
- danielutils/functions/parallel_for.py +47 -0
- danielutils/functions/partition.py +45 -0
- danielutils/functions/powerset.py +31 -0
- danielutils/functions/subseteq.py +25 -0
- danielutils/functions/types_subseteq.py +51 -0
- danielutils/generators/__init__.py +3 -0
- danielutils/generators/conditional_generator.py +59 -0
- danielutils/generators/generator_from_stream.py +31 -0
- danielutils/generators/join_generators.py +109 -0
- danielutils/internet.py +92 -0
- danielutils/io_.py +451 -0
- danielutils/java/__init__.py +18 -0
- danielutils/java/interfaces/__init__.py +1 -0
- danielutils/java/interfaces/comparable.py +31 -0
- danielutils/java/java_interface.py +59 -0
- danielutils/logging_/__init__.py +2 -0
- danielutils/logging_/_impl/__init__.py +4 -0
- danielutils/logging_/_impl/builtin_impls/__init__.py +2 -0
- danielutils/logging_/_impl/builtin_impls/file_logger.py +35 -0
- danielutils/logging_/_impl/builtin_impls/print_logger.py +21 -0
- danielutils/logging_/_impl/log_level.py +13 -0
- danielutils/logging_/_impl/logger.py +109 -0
- danielutils/logging_/_impl/logger_strategy_impl_base.py +37 -0
- danielutils/logging_/utils.py +199 -0
- danielutils/lombok/__init__.py +1 -0
- danielutils/lombok/builder.py +68 -0
- danielutils/math_/__init__.py +2 -0
- danielutils/math_/constants.py +103 -0
- danielutils/math_/functions.py +34 -0
- danielutils/math_/math_print.py +52 -0
- danielutils/math_/math_symbols.py +125 -0
- danielutils/metaclasses/__init__.py +5 -0
- danielutils/metaclasses/atomic_class_meta.py +36 -0
- danielutils/metaclasses/implicit_data_deleter_meta.py +87 -0
- danielutils/metaclasses/instance_cache_meta.py +66 -0
- danielutils/metaclasses/interface.py +328 -0
- danielutils/metaclasses/overload_meta.py +48 -0
- danielutils/mock_/__init__.py +1 -0
- danielutils/mock_/mock_module.py +27 -0
- danielutils/path.py +55 -0
- danielutils/print_.py +123 -0
- danielutils/progress_bar/__init__.py +3 -0
- danielutils/progress_bar/ascii_progress_bar.py +161 -0
- danielutils/progress_bar/progress_bar.py +75 -0
- danielutils/progress_bar/progress_bar_pool.py +72 -0
- danielutils/protocols/__init__.py +3 -0
- danielutils/protocols/dictable.py +38 -0
- danielutils/protocols/evaluable.py +26 -0
- danielutils/protocols/serializable.py +51 -0
- danielutils/py.typed +0 -0
- danielutils/random_.py +47 -0
- danielutils/reflection/__init__.py +6 -0
- danielutils/reflection/declarations.py +288 -0
- danielutils/reflection/file/__init__.py +1 -0
- danielutils/reflection/file/file_reflection.py +85 -0
- danielutils/reflection/function/__init__.py +1 -0
- danielutils/reflection/function/function_reflections.py +152 -0
- danielutils/reflection/info_classes/__init__.py +6 -0
- danielutils/reflection/info_classes/argument_info.py +278 -0
- danielutils/reflection/info_classes/class_info.py +204 -0
- danielutils/reflection/info_classes/decorator_info.py +54 -0
- danielutils/reflection/info_classes/file_info.py +1180 -0
- danielutils/reflection/info_classes/folder_info.py +592 -0
- danielutils/reflection/info_classes/function_info.py +551 -0
- danielutils/reflection/info_classes/import_info.py +207 -0
- danielutils/reflection/interpreter/__init__.py +9 -0
- danielutils/reflection/interpreter/callstack.py +57 -0
- danielutils/reflection/interpreter/get_traceback.py +27 -0
- danielutils/reflection/interpreter/interpreter.py +41 -0
- danielutils/reflection/interpreter/is_debugging.py +13 -0
- danielutils/reflection/interpreter/os_.py +33 -0
- danielutils/reflection/interpreter/packages.py +33 -0
- danielutils/reflection/interpreter/python_version.py +33 -0
- danielutils/reflection/interpreter/signals.py +18 -0
- danielutils/reflection/interpreter/tracer.py +175 -0
- danielutils/reflection/module/__init__.py +3 -0
- danielutils/reflection/module/lazy_module.py +104 -0
- danielutils/reflection/module/module_reflections.py +34 -0
- danielutils/reflection/module/package_reflection.py +161 -0
- danielutils/retry_executor/__init__.py +3 -0
- danielutils/retry_executor/backoff_strategies/__init__.py +6 -0
- danielutils/retry_executor/backoff_strategies/constant_backoff.py +25 -0
- danielutils/retry_executor/backoff_strategies/exponential_backoff.py +26 -0
- danielutils/retry_executor/backoff_strategies/functional_backoff.py +29 -0
- danielutils/retry_executor/backoff_strategies/linear_backoff.py +23 -0
- danielutils/retry_executor/backoff_strategies/multiplicative_backoff.py +23 -0
- danielutils/retry_executor/backoff_strategies/no_backoff.py +17 -0
- danielutils/retry_executor/backoff_strategy.py +28 -0
- danielutils/retry_executor/retry_executor.py +55 -0
- danielutils/snippets/__init__.py +1 -0
- danielutils/snippets/try_get.py +28 -0
- danielutils/system/__init__.py +3 -0
- danielutils/system/independent.py +197 -0
- danielutils/system/layered_command.py +130 -0
- danielutils/system/windows/__init__.py +2 -0
- danielutils/system/windows/utils/__init__.py +1 -0
- danielutils/system/windows/utils/filetime.py +19 -0
- danielutils/system/windows/win32_ctime.py +183 -0
- danielutils/system/windows/windows.py +78 -0
- danielutils/testing/__init__.py +1 -0
- danielutils/testing/unittest_/__init__.py +2 -0
- danielutils/testing/unittest_/always_teardown_testcase.py +75 -0
- danielutils/testing/unittest_/auto_cwd_testcase.py +114 -0
- danielutils/text.py +169 -0
- danielutils/time.py +64 -0
- danielutils/tools/__init__.py +1 -0
- danielutils/tools/unittest_test_runner/__init__.py +9 -0
- danielutils/tools/unittest_test_runner/cli.py +70 -0
- danielutils/tools/unittest_test_runner/core/__init__.py +17 -0
- danielutils/tools/unittest_test_runner/core/discovery.py +322 -0
- danielutils/tools/unittest_test_runner/core/execution.py +244 -0
- danielutils/tools/unittest_test_runner/core/parser.py +163 -0
- danielutils/tools/unittest_test_runner/core/runner.py +561 -0
- danielutils/tools/unittest_test_runner/models.py +82 -0
- danielutils/university/__init__.py +7 -0
- danielutils/university/computability_and_complexity/__init__.py +2 -0
- danielutils/university/computability_and_complexity/discreate_finite_automaton.py +89 -0
- danielutils/university/computability_and_complexity/languages/__init__.py +0 -0
- danielutils/university/computability_and_complexity/languages/language.py +17 -0
- danielutils/university/computability_and_complexity/languages/sat.py +71 -0
- danielutils/university/computability_and_complexity/turing_machine.py +47 -0
- danielutils/university/databases/__init__.py +1 -0
- danielutils/university/databases/all.py +756 -0
- danielutils/university/image_proccesing/__init__.py +1 -0
- danielutils/university/image_proccesing/encoding/__init__.py +3 -0
- danielutils/university/image_proccesing/encoding/encoding.py +35 -0
- danielutils/university/image_proccesing/encoding/lossless/__init__.py +4 -0
- danielutils/university/image_proccesing/encoding/lossless/huffman.py +0 -0
- danielutils/university/image_proccesing/encoding/lossless/lossless_encoding.py +10 -0
- danielutils/university/image_proccesing/encoding/lossless/lzw.py +0 -0
- danielutils/university/image_proccesing/encoding/lossless/run_length.py +22 -0
- danielutils/university/image_proccesing/encoding/lossy/__init__.py +1 -0
- danielutils/university/image_proccesing/encoding/lossy/lossy_encoding.py +10 -0
- danielutils/university/image_proccesing/tansformations/__init__.py +0 -0
- danielutils/university/image_proccesing/tansformations/gaussian.py +0 -0
- danielutils/university/image_proccesing/tansformations/gradient.py +0 -0
- danielutils/university/image_proccesing/tansformations/hough.py +0 -0
- danielutils/university/image_proccesing/tansformations/laplacian.py +0 -0
- danielutils/university/image_proccesing/tansformations/transformation.py +11 -0
- danielutils/university/linear_algebra/__init__.py +1 -0
- danielutils/university/linear_algebra/matrix.py +404 -0
- danielutils/university/machine_learning/__init__.py +0 -0
- danielutils/university/machine_learning/activation_functions/__init__.py +2 -0
- danielutils/university/machine_learning/activation_functions/activation_function.py +11 -0
- danielutils/university/machine_learning/activation_functions/relu.py +11 -0
- danielutils/university/machine_learning/neuron.py +15 -0
- danielutils/university/oop/__init__.py +1 -0
- danielutils/university/oop/observer.py +31 -0
- danielutils/university/oop/strategy.py +9 -0
- danielutils/university/probability/__init__.py +7 -0
- danielutils/university/probability/conditional_variable/__init__.py +3 -0
- danielutils/university/probability/conditional_variable/conditional_variable.py +138 -0
- danielutils/university/probability/conditional_variable/continuous/__init__.py +0 -0
- danielutils/university/probability/conditional_variable/discrete/__init__.py +8 -0
- danielutils/university/probability/conditional_variable/discrete/bernoulli.py +38 -0
- danielutils/university/probability/conditional_variable/discrete/bernoulli_sum.py +31 -0
- danielutils/university/probability/conditional_variable/discrete/binomial.py +40 -0
- danielutils/university/probability/conditional_variable/discrete/conditional_from_discrete_probability_func.py +36 -0
- danielutils/university/probability/conditional_variable/discrete/discrete.py +42 -0
- danielutils/university/probability/conditional_variable/discrete/geometric.py +43 -0
- danielutils/university/probability/conditional_variable/discrete/poisson.py +32 -0
- danielutils/university/probability/conditional_variable/discrete/uniform.py +33 -0
- danielutils/university/probability/distributions.py +35 -0
- danielutils/university/probability/expressions/__init__.py +2 -0
- danielutils/university/probability/expressions/accumulation_expression.py +291 -0
- danielutils/university/probability/expressions/probability_expression.py +110 -0
- danielutils/university/probability/funcs/__init__.py +4 -0
- danielutils/university/probability/funcs/covariance.py +12 -0
- danielutils/university/probability/funcs/expected_value.py +41 -0
- danielutils/university/probability/funcs/probability_function.py +14 -0
- danielutils/university/probability/funcs/variance.py +17 -0
- danielutils/university/probability/operator.py +79 -0
- danielutils/university/probability/protocols.py +32 -0
- danielutils/university/probability/supp.py +101 -0
- danielutils/university/probability/transformation.py +44 -0
- danielutils/versioned_imports.py +31 -0
- danielutils-1.1.28.dist-info/METADATA +390 -0
- danielutils-1.1.28.dist-info/RECORD +287 -0
- danielutils-1.1.28.dist-info/WHEEL +5 -0
- danielutils-1.1.28.dist-info/licenses/LICENSE +21 -0
- 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,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
|
+
]
|